From 99e9c0e058d1304a1cc942ca881434c9c358112b Mon Sep 17 00:00:00 2001 From: GunSik Choi Date: Tue, 4 Feb 2025 06:18:32 +0000 Subject: [PATCH] add open-webui chart --- charts/open-webui/Chart.lock | 15 + charts/open-webui/Chart.yaml | 44 + charts/open-webui/README.md | 126 + charts/open-webui/README.md.gotmpl | 36 + charts/open-webui/charts/ollama/.helmignore | 30 + charts/open-webui/charts/ollama/Chart.yaml | 28 + charts/open-webui/charts/ollama/LICENSE | 21 + charts/open-webui/charts/ollama/README.md | 295 + .../charts/ollama/artifacthub-repo.yml | 15 + charts/open-webui/charts/ollama/banner.png | Bin 0 -> 134943 bytes .../charts/ollama/templates/NOTES.txt | 25 + .../charts/ollama/templates/_helpers.tpl | 91 + .../charts/ollama/templates/deployment.yaml | 213 + .../charts/ollama/templates/hpa.yaml | 34 + .../charts/ollama/templates/ingress.yaml | 63 + .../ollama/templates/knative/service.yaml | 156 + .../charts/ollama/templates/pvc.yaml | 35 + .../charts/ollama/templates/service.yaml | 29 + .../ollama/templates/serviceaccount.yaml | 15 + .../templates/tests/test-connection.yaml | 17 + .../open-webui/charts/ollama/values-test.yaml | 311 + charts/open-webui/charts/ollama/values.yaml | 370 ++ .../open-webui/charts/pipelines/.helmignore | 24 + charts/open-webui/charts/pipelines/Chart.yaml | 16 + charts/open-webui/charts/pipelines/README.md | 71 + .../charts/pipelines/README.md.gotmpl | 36 + .../charts/pipelines/templates/_helpers.tpl | 65 + .../pipelines/templates/deployment.yaml | 79 + .../charts/pipelines/templates/pvc.yaml | 28 + .../pipelines/templates/service-account.yaml | 13 + .../charts/pipelines/templates/service.yaml | 30 + .../open-webui/charts/pipelines/values.yaml | 88 + charts/open-webui/charts/redis/.helmignore | 25 + charts/open-webui/charts/redis/Chart.lock | 6 + charts/open-webui/charts/redis/Chart.yaml | 38 + charts/open-webui/charts/redis/README.md | 1321 ++++ .../charts/redis/charts/common/.helmignore | 26 + .../charts/redis/charts/common/Chart.yaml | 23 + .../charts/redis/charts/common/README.md | 235 + .../charts/common/templates/_affinities.tpl | 155 + .../charts/common/templates/_capabilities.tpl | 239 + .../common/templates/_compatibility.tpl | 46 + .../redis/charts/common/templates/_errors.tpl | 85 + .../redis/charts/common/templates/_images.tpl | 115 + .../charts/common/templates/_ingress.tpl | 73 + .../redis/charts/common/templates/_labels.tpl | 46 + .../redis/charts/common/templates/_names.tpl | 71 + .../charts/common/templates/_resources.tpl | 50 + .../charts/common/templates/_secrets.tpl | 192 + .../charts/common/templates/_storage.tpl | 21 + .../charts/common/templates/_tplvalues.tpl | 52 + .../redis/charts/common/templates/_utils.tpl | 77 + .../charts/common/templates/_warnings.tpl | 109 + .../templates/validations/_cassandra.tpl | 51 + .../common/templates/validations/_mariadb.tpl | 108 + .../common/templates/validations/_mongodb.tpl | 67 + .../common/templates/validations/_mysql.tpl | 67 + .../templates/validations/_postgresql.tpl | 105 + .../common/templates/validations/_redis.tpl | 48 + .../templates/validations/_validations.tpl | 51 + .../charts/redis/charts/common/values.yaml | 8 + .../charts/redis/templates/NOTES.txt | 216 + .../charts/redis/templates/_helpers.tpl | 325 + .../charts/redis/templates/configmap.yaml | 64 + .../charts/redis/templates/extra-list.yaml | 9 + .../charts/redis/templates/headless-svc.yaml | 38 + .../redis/templates/health-configmap.yaml | 204 + .../redis/templates/master/application.yaml | 558 ++ .../charts/redis/templates/master/pdb.yaml | 27 + .../charts/redis/templates/master/psp.yaml | 47 + .../charts/redis/templates/master/pvc.yaml | 33 + .../redis/templates/master/service.yaml | 62 + .../templates/master/serviceaccount.yaml | 18 + .../charts/redis/templates/metrics-svc.yaml | 44 + .../charts/redis/templates/networkpolicy.yaml | 108 + .../charts/redis/templates/podmonitor.yaml | 81 + .../redis/templates/prometheusrule.yaml | 23 + .../redis/templates/replicas/application.yaml | 573 ++ .../charts/redis/templates/replicas/hpa.yaml | 49 + .../charts/redis/templates/replicas/pdb.yaml | 28 + .../redis/templates/replicas/service.yaml | 59 + .../templates/replicas/serviceaccount.yaml | 18 + .../charts/redis/templates/role.yaml | 34 + .../charts/redis/templates/rolebinding.yaml | 23 + .../redis/templates/scripts-configmap.yaml | 811 +++ .../redis/templates/secret-svcbind.yaml | 37 + .../charts/redis/templates/secret.yaml | 25 + .../charts/redis/templates/sentinel/hpa.yaml | 49 + .../templates/sentinel/node-services.yaml | 67 + .../charts/redis/templates/sentinel/pdb.yaml | 27 + .../templates/sentinel/ports-configmap.yaml | 102 + .../redis/templates/sentinel/service.yaml | 160 + .../redis/templates/sentinel/statefulset.yaml | 851 +++ .../redis/templates/serviceaccount.yaml | 18 + .../redis/templates/servicemonitor.yaml | 82 + .../charts/redis/templates/tls-secret.yaml | 31 + .../charts/redis/values.schema.json | 3281 ++++++++++ charts/open-webui/charts/redis/values.yaml | 2270 +++++++ charts/open-webui/charts/tika/.asf.yaml | 26 + .../tika/.github/workflows/lint-test.yaml | 64 + charts/open-webui/charts/tika/.helmignore | 23 + charts/open-webui/charts/tika/Chart.yaml | 28 + charts/open-webui/charts/tika/LICENSE.txt | 5737 +++++++++++++++++ charts/open-webui/charts/tika/README.md | 146 + .../charts/tika/templates/NOTES.txt | 22 + .../charts/tika/templates/_helpers.tpl | 62 + .../charts/tika/templates/deployment.yaml | 95 + .../open-webui/charts/tika/templates/hpa.yaml | 65 + .../charts/tika/templates/ingress.yaml | 79 + .../charts/tika/templates/networkpolicy.yaml | 23 + .../charts/tika/templates/service.yaml | 33 + .../charts/tika/templates/serviceaccount.yaml | 30 + .../tika/templates/tests/test-connection.yaml | 33 + charts/open-webui/charts/tika/values.yaml | 67 + charts/open-webui/custom-values.yaml | 57 + charts/open-webui/templates/NOTES.txt | 77 + charts/open-webui/templates/_helpers.tpl | 171 + .../open-webui/templates/extra-resources.yaml | 6 + charts/open-webui/templates/ingress.yaml | 49 + charts/open-webui/templates/pvc.yaml | 28 + .../open-webui/templates/service-account.yaml | 14 + charts/open-webui/templates/service.yaml | 36 + .../open-webui/templates/websocket-redis.yaml | 71 + .../templates/workload-manager.yaml | 178 + charts/open-webui/values.yaml | 276 + 125 files changed, 23877 insertions(+) create mode 100644 charts/open-webui/Chart.lock create mode 100644 charts/open-webui/Chart.yaml create mode 100644 charts/open-webui/README.md create mode 100644 charts/open-webui/README.md.gotmpl create mode 100644 charts/open-webui/charts/ollama/.helmignore create mode 100644 charts/open-webui/charts/ollama/Chart.yaml create mode 100644 charts/open-webui/charts/ollama/LICENSE create mode 100644 charts/open-webui/charts/ollama/README.md create mode 100644 charts/open-webui/charts/ollama/artifacthub-repo.yml create mode 100644 charts/open-webui/charts/ollama/banner.png create mode 100644 charts/open-webui/charts/ollama/templates/NOTES.txt create mode 100644 charts/open-webui/charts/ollama/templates/_helpers.tpl create mode 100644 charts/open-webui/charts/ollama/templates/deployment.yaml create mode 100644 charts/open-webui/charts/ollama/templates/hpa.yaml create mode 100644 charts/open-webui/charts/ollama/templates/ingress.yaml create mode 100644 charts/open-webui/charts/ollama/templates/knative/service.yaml create mode 100644 charts/open-webui/charts/ollama/templates/pvc.yaml create mode 100644 charts/open-webui/charts/ollama/templates/service.yaml create mode 100644 charts/open-webui/charts/ollama/templates/serviceaccount.yaml create mode 100644 charts/open-webui/charts/ollama/templates/tests/test-connection.yaml create mode 100644 charts/open-webui/charts/ollama/values-test.yaml create mode 100644 charts/open-webui/charts/ollama/values.yaml create mode 100644 charts/open-webui/charts/pipelines/.helmignore create mode 100644 charts/open-webui/charts/pipelines/Chart.yaml create mode 100644 charts/open-webui/charts/pipelines/README.md create mode 100644 charts/open-webui/charts/pipelines/README.md.gotmpl create mode 100644 charts/open-webui/charts/pipelines/templates/_helpers.tpl create mode 100644 charts/open-webui/charts/pipelines/templates/deployment.yaml create mode 100644 charts/open-webui/charts/pipelines/templates/pvc.yaml create mode 100644 charts/open-webui/charts/pipelines/templates/service-account.yaml create mode 100644 charts/open-webui/charts/pipelines/templates/service.yaml create mode 100644 charts/open-webui/charts/pipelines/values.yaml create mode 100644 charts/open-webui/charts/redis/.helmignore create mode 100644 charts/open-webui/charts/redis/Chart.lock create mode 100644 charts/open-webui/charts/redis/Chart.yaml create mode 100644 charts/open-webui/charts/redis/README.md create mode 100644 charts/open-webui/charts/redis/charts/common/.helmignore create mode 100644 charts/open-webui/charts/redis/charts/common/Chart.yaml create mode 100644 charts/open-webui/charts/redis/charts/common/README.md create mode 100644 charts/open-webui/charts/redis/charts/common/templates/_affinities.tpl create mode 100644 charts/open-webui/charts/redis/charts/common/templates/_capabilities.tpl create mode 100644 charts/open-webui/charts/redis/charts/common/templates/_compatibility.tpl create mode 100644 charts/open-webui/charts/redis/charts/common/templates/_errors.tpl create mode 100644 charts/open-webui/charts/redis/charts/common/templates/_images.tpl create mode 100644 charts/open-webui/charts/redis/charts/common/templates/_ingress.tpl create mode 100644 charts/open-webui/charts/redis/charts/common/templates/_labels.tpl create mode 100644 charts/open-webui/charts/redis/charts/common/templates/_names.tpl create mode 100644 charts/open-webui/charts/redis/charts/common/templates/_resources.tpl create mode 100644 charts/open-webui/charts/redis/charts/common/templates/_secrets.tpl create mode 100644 charts/open-webui/charts/redis/charts/common/templates/_storage.tpl create mode 100644 charts/open-webui/charts/redis/charts/common/templates/_tplvalues.tpl create mode 100644 charts/open-webui/charts/redis/charts/common/templates/_utils.tpl create mode 100644 charts/open-webui/charts/redis/charts/common/templates/_warnings.tpl create mode 100644 charts/open-webui/charts/redis/charts/common/templates/validations/_cassandra.tpl create mode 100644 charts/open-webui/charts/redis/charts/common/templates/validations/_mariadb.tpl create mode 100644 charts/open-webui/charts/redis/charts/common/templates/validations/_mongodb.tpl create mode 100644 charts/open-webui/charts/redis/charts/common/templates/validations/_mysql.tpl create mode 100644 charts/open-webui/charts/redis/charts/common/templates/validations/_postgresql.tpl create mode 100644 charts/open-webui/charts/redis/charts/common/templates/validations/_redis.tpl create mode 100644 charts/open-webui/charts/redis/charts/common/templates/validations/_validations.tpl create mode 100644 charts/open-webui/charts/redis/charts/common/values.yaml create mode 100644 charts/open-webui/charts/redis/templates/NOTES.txt create mode 100644 charts/open-webui/charts/redis/templates/_helpers.tpl create mode 100644 charts/open-webui/charts/redis/templates/configmap.yaml create mode 100644 charts/open-webui/charts/redis/templates/extra-list.yaml create mode 100644 charts/open-webui/charts/redis/templates/headless-svc.yaml create mode 100644 charts/open-webui/charts/redis/templates/health-configmap.yaml create mode 100644 charts/open-webui/charts/redis/templates/master/application.yaml create mode 100644 charts/open-webui/charts/redis/templates/master/pdb.yaml create mode 100644 charts/open-webui/charts/redis/templates/master/psp.yaml create mode 100644 charts/open-webui/charts/redis/templates/master/pvc.yaml create mode 100644 charts/open-webui/charts/redis/templates/master/service.yaml create mode 100644 charts/open-webui/charts/redis/templates/master/serviceaccount.yaml create mode 100644 charts/open-webui/charts/redis/templates/metrics-svc.yaml create mode 100644 charts/open-webui/charts/redis/templates/networkpolicy.yaml create mode 100644 charts/open-webui/charts/redis/templates/podmonitor.yaml create mode 100644 charts/open-webui/charts/redis/templates/prometheusrule.yaml create mode 100644 charts/open-webui/charts/redis/templates/replicas/application.yaml create mode 100644 charts/open-webui/charts/redis/templates/replicas/hpa.yaml create mode 100644 charts/open-webui/charts/redis/templates/replicas/pdb.yaml create mode 100644 charts/open-webui/charts/redis/templates/replicas/service.yaml create mode 100644 charts/open-webui/charts/redis/templates/replicas/serviceaccount.yaml create mode 100644 charts/open-webui/charts/redis/templates/role.yaml create mode 100644 charts/open-webui/charts/redis/templates/rolebinding.yaml create mode 100644 charts/open-webui/charts/redis/templates/scripts-configmap.yaml create mode 100644 charts/open-webui/charts/redis/templates/secret-svcbind.yaml create mode 100644 charts/open-webui/charts/redis/templates/secret.yaml create mode 100644 charts/open-webui/charts/redis/templates/sentinel/hpa.yaml create mode 100644 charts/open-webui/charts/redis/templates/sentinel/node-services.yaml create mode 100644 charts/open-webui/charts/redis/templates/sentinel/pdb.yaml create mode 100644 charts/open-webui/charts/redis/templates/sentinel/ports-configmap.yaml create mode 100644 charts/open-webui/charts/redis/templates/sentinel/service.yaml create mode 100644 charts/open-webui/charts/redis/templates/sentinel/statefulset.yaml create mode 100644 charts/open-webui/charts/redis/templates/serviceaccount.yaml create mode 100644 charts/open-webui/charts/redis/templates/servicemonitor.yaml create mode 100644 charts/open-webui/charts/redis/templates/tls-secret.yaml create mode 100644 charts/open-webui/charts/redis/values.schema.json create mode 100644 charts/open-webui/charts/redis/values.yaml create mode 100644 charts/open-webui/charts/tika/.asf.yaml create mode 100644 charts/open-webui/charts/tika/.github/workflows/lint-test.yaml create mode 100644 charts/open-webui/charts/tika/.helmignore create mode 100644 charts/open-webui/charts/tika/Chart.yaml create mode 100644 charts/open-webui/charts/tika/LICENSE.txt create mode 100644 charts/open-webui/charts/tika/README.md create mode 100644 charts/open-webui/charts/tika/templates/NOTES.txt create mode 100644 charts/open-webui/charts/tika/templates/_helpers.tpl create mode 100644 charts/open-webui/charts/tika/templates/deployment.yaml create mode 100644 charts/open-webui/charts/tika/templates/hpa.yaml create mode 100644 charts/open-webui/charts/tika/templates/ingress.yaml create mode 100644 charts/open-webui/charts/tika/templates/networkpolicy.yaml create mode 100644 charts/open-webui/charts/tika/templates/service.yaml create mode 100644 charts/open-webui/charts/tika/templates/serviceaccount.yaml create mode 100644 charts/open-webui/charts/tika/templates/tests/test-connection.yaml create mode 100644 charts/open-webui/charts/tika/values.yaml create mode 100644 charts/open-webui/custom-values.yaml create mode 100644 charts/open-webui/templates/NOTES.txt create mode 100644 charts/open-webui/templates/_helpers.tpl create mode 100644 charts/open-webui/templates/extra-resources.yaml create mode 100644 charts/open-webui/templates/ingress.yaml create mode 100644 charts/open-webui/templates/pvc.yaml create mode 100644 charts/open-webui/templates/service-account.yaml create mode 100644 charts/open-webui/templates/service.yaml create mode 100644 charts/open-webui/templates/websocket-redis.yaml create mode 100644 charts/open-webui/templates/workload-manager.yaml create mode 100644 charts/open-webui/values.yaml diff --git a/charts/open-webui/Chart.lock b/charts/open-webui/Chart.lock new file mode 100644 index 0000000..dd9defd --- /dev/null +++ b/charts/open-webui/Chart.lock @@ -0,0 +1,15 @@ +dependencies: +- name: ollama + repository: https://otwld.github.io/ollama-helm/ + version: 1.3.0 +- name: pipelines + repository: https://helm.openwebui.com + version: 0.0.6 +- name: tika + repository: https://apache.jfrog.io/artifactory/tika + version: 2.9.0 +- name: redis + repository: https://charts.bitnami.com/bitnami + version: 20.6.3 +digest: sha256:b0e3a4613e4f5b9ad2766cafb111b2c03dff1ba1774217ed21fb9fd5556729f4 +generated: "2025-01-16T14:22:56.662663-07:00" diff --git a/charts/open-webui/Chart.yaml b/charts/open-webui/Chart.yaml new file mode 100644 index 0000000..b6d2e4e --- /dev/null +++ b/charts/open-webui/Chart.yaml @@ -0,0 +1,44 @@ +annotations: + licenses: MIT +apiVersion: v2 +appVersion: 0.5.4 +dependencies: +- condition: ollama.enabled + import-values: + - child: service + parent: ollama.service + name: ollama + repository: https://otwld.github.io/ollama-helm/ + version: '>=0.24.0' +- condition: pipelines.enabled + import-values: + - child: service + parent: pipelines.service + name: pipelines + repository: https://helm.openwebui.com + version: '>=0.0.1' +- condition: tika.enabled + name: tika + repository: https://apache.jfrog.io/artifactory/tika + version: '>=2.9.0' +- alias: redis-cluster + condition: redis-cluster.enabled + name: redis + repository: https://charts.bitnami.com/bitnami + version: '>=20.6.2' +description: "Open WebUI: A User-Friendly Web Interface for Chat Interactions \U0001F44B" +home: https://www.openwebui.com/ +icon: https://raw.githubusercontent.com/open-webui/open-webui/main/static/favicon.png +keywords: +- llm +- chat +- web-ui +- open-webui +name: open-webui +sources: +- https://github.com/open-webui/helm-charts +- https://github.com/open-webui/open-webui/pkgs/container/open-webui +- https://github.com/otwld/ollama-helm/ +- https://hub.docker.com/r/ollama/ollama +- https://charts.bitnami.com/bitnami +version: 5.4.0 diff --git a/charts/open-webui/README.md b/charts/open-webui/README.md new file mode 100644 index 0000000..ef4a5b4 --- /dev/null +++ b/charts/open-webui/README.md @@ -0,0 +1,126 @@ +# open-webui + +![Version: 5.4.0](https://img.shields.io/badge/Version-5.4.0-informational?style=flat-square) ![AppVersion: 0.5.4](https://img.shields.io/badge/AppVersion-0.5.4-informational?style=flat-square) + +Open WebUI: A User-Friendly Web Interface for Chat Interactions 👋 + +**Homepage:** + +## Source Code + +* +* +* +* + +## Installing + +Before you can install, you need to add the `open-webui` repo to [Helm](https://helm.sh) + +```shell +helm repo add open-webui https://helm.openwebui.com/ +helm repo update +``` + +Now you can install the chart: + +```shell +helm upgrade --install open-webui open-webui/open-webui +``` + +## Requirements + +| Repository | Name | Version | +|------------|------|---------| +| https://apache.jfrog.io/artifactory/tika | tika | >=2.9.0 | +| https://charts.bitnami.com/bitnami | redis-cluster(redis) | >=20.6.2 | +| https://helm.openwebui.com | pipelines | >=0.0.1 | +| https://otwld.github.io/ollama-helm/ | ollama | >=0.24.0 | + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | Affinity for pod assignment | +| annotations | object | `{}` | | +| clusterDomain | string | `"cluster.local"` | Value of cluster domain | +| containerSecurityContext | object | `{}` | Configure container security context ref: | +| copyAppData.resources | object | `{}` | | +| extraEnvVars | list | `[{"name":"OPENAI_API_KEY","value":"0p3n-w3bu!"}]` | Env vars added to the Open WebUI deployment. Most up-to-date environment variables can be found here: https://docs.openwebui.com/getting-started/env-configuration/ | +| extraEnvVars[0] | object | `{"name":"OPENAI_API_KEY","value":"0p3n-w3bu!"}` | Default API key value for Pipelines. Should be updated in a production deployment, or be changed to the required API key if not using Pipelines | +| extraResources | list | `[]` | Extra resources to deploy with Open WebUI | +| image | object | `{"pullPolicy":"IfNotPresent","repository":"ghcr.io/open-webui/open-webui","tag":""}` | Open WebUI image tags can be found here: https://github.com/open-webui/open-webui | +| imagePullSecrets | list | `[]` | Configure imagePullSecrets to use private registry ref: | +| ingress.additionalHosts | list | `[]` | | +| ingress.annotations | object | `{}` | Use appropriate annotations for your Ingress controller, e.g., for NGINX: nginx.ingress.kubernetes.io/rewrite-target: / | +| ingress.class | string | `""` | | +| ingress.enabled | bool | `false` | | +| ingress.existingSecret | string | `""` | | +| ingress.host | string | `""` | | +| ingress.tls | bool | `false` | | +| livenessProbe | object | `{}` | Probe for liveness of the Open WebUI container ref: | +| nameOverride | string | `""` | | +| namespaceOverride | string | `""` | | +| nodeSelector | object | `{}` | Node labels for pod assignment. | +| ollama.enabled | bool | `true` | Automatically install Ollama Helm chart from https://otwld.github.io/ollama-helm/. Use [Helm Values](https://github.com/otwld/ollama-helm/#helm-values) to configure | +| ollama.fullnameOverride | string | `"open-webui-ollama"` | If enabling embedded Ollama, update fullnameOverride to your desired Ollama name value, or else it will use the default ollama.name value from the Ollama chart | +| ollamaUrls | list | `[]` | A list of Ollama API endpoints. These can be added in lieu of automatically installing the Ollama Helm chart, or in addition to it. | +| openaiBaseApiUrl | string | `""` | OpenAI base API URL to use. Defaults to the Pipelines service endpoint when Pipelines are enabled, and "https://api.openai.com/v1" if Pipelines are not enabled and this value is blank | +| persistence.accessModes | list | `["ReadWriteOnce"]` | If using multiple replicas, you must update accessModes to ReadWriteMany | +| persistence.annotations | object | `{}` | | +| persistence.enabled | bool | `true` | | +| persistence.existingClaim | string | `""` | Use existingClaim if you want to re-use an existing Open WebUI PVC instead of creating a new one | +| persistence.selector | object | `{}` | | +| persistence.size | string | `"2Gi"` | | +| persistence.storageClass | string | `""` | | +| persistence.subPath | string | `""` | Subdirectory of Open WebUI PVC to mount. Useful if root directory is not empty. | +| pipelines.enabled | bool | `true` | Automatically install Pipelines chart to extend Open WebUI functionality using Pipelines: https://github.com/open-webui/pipelines | +| pipelines.extraEnvVars | list | `[]` | This section can be used to pass required environment variables to your pipelines (e.g. Langfuse hostname) | +| podAnnotations | object | `{}` | | +| podLabels | object | `{}` | | +| podSecurityContext | object | `{}` | Configure pod security context ref: | +| readinessProbe | object | `{}` | Probe for readiness of the Open WebUI container ref: | +| redis-cluster | object | `{"auth":{"enabled":false},"enabled":false,"fullnameOverride":"open-webui-redis","replica":{"replicaCount":3}}` | Deploys a Redis cluster with subchart 'redis' from bitnami | +| redis-cluster.auth | object | `{"enabled":false}` | Redis Authentication | +| redis-cluster.auth.enabled | bool | `false` | Enable Redis authentication (disabled by default). For your security, we strongly suggest that you switch to 'auth.enabled=true' | +| redis-cluster.enabled | bool | `false` | Enable Redis installation | +| redis-cluster.fullnameOverride | string | `"open-webui-redis"` | Redis cluster name (recommended to be 'open-webui-redis') - In this case, redis url will be 'redis://open-webui-redis-master:6379/0' or 'redis://[:@]open-webui-redis-master:6379/0' | +| redis-cluster.replica | object | `{"replicaCount":3}` | Replica configuration for the Redis cluster | +| redis-cluster.replica.replicaCount | int | `3` | Number of Redis replica instances | +| replicaCount | int | `1` | | +| resources | object | `{}` | | +| service | object | `{"annotations":{},"containerPort":8080,"labels":{},"loadBalancerClass":"","nodePort":"","port":80,"type":"ClusterIP"}` | Service values to expose Open WebUI pods to cluster | +| serviceAccount.annotations | object | `{}` | | +| serviceAccount.automountServiceAccountToken | bool | `false` | | +| serviceAccount.enable | bool | `true` | | +| serviceAccount.name | string | `""` | | +| startupProbe | object | `{}` | Probe for startup of the Open WebUI container ref: | +| strategy | object | `{}` | Strategy for updating the workload manager: deployment or statefulset | +| tika.enabled | bool | `false` | Automatically install Apache Tika to extend Open WebUI | +| tolerations | list | `[]` | Tolerations for pod assignment | +| topologySpreadConstraints | list | `[]` | Topology Spread Constraints for pod assignment | +| volumeMounts | object | `{"container":[],"initContainer":[]}` | Configure container volume mounts ref: | +| volumes | list | `[]` | Configure pod volumes ref: | +| websocket.enabled | bool | `false` | Enables websocket support in Open WebUI with env `ENABLE_WEBSOCKET_SUPPORT` | +| websocket.manager | string | `"redis"` | Specifies the websocket manager to use with env `WEBSOCKET_MANAGER`: redis (default) | +| websocket.redis | object | `{"annotations":{},"args":[],"command":[],"enabled":true,"image":{"pullPolicy":"IfNotPresent","repository":"redis","tag":"7.4.2-alpine3.21"},"labels":{},"name":"open-webui-redis","resources":{},"service":{"annotations":{},"containerPort":6379,"labels":{},"nodePort":"","port":6379,"type":"ClusterIP"}}` | Deploys a redis | +| websocket.redis.annotations | object | `{}` | Redis annotations | +| websocket.redis.args | list | `[]` | Redis arguments (overrides default) | +| websocket.redis.command | list | `[]` | Redis command (overrides default) | +| websocket.redis.enabled | bool | `true` | Enable redis installation | +| websocket.redis.image | object | `{"pullPolicy":"IfNotPresent","repository":"redis","tag":"7.4.2-alpine3.21"}` | Redis image | +| websocket.redis.labels | object | `{}` | Redis labels | +| websocket.redis.name | string | `"open-webui-redis"` | Redis name | +| websocket.redis.resources | object | `{}` | Redis resources | +| websocket.redis.service | object | `{"annotations":{},"containerPort":6379,"labels":{},"nodePort":"","port":6379,"type":"ClusterIP"}` | Redis service | +| websocket.redis.service.annotations | object | `{}` | Redis service annotations | +| websocket.redis.service.containerPort | int | `6379` | Redis container/target port | +| websocket.redis.service.labels | object | `{}` | Redis service labels | +| websocket.redis.service.nodePort | string | `""` | Redis service node port. Valid only when type is `NodePort` | +| websocket.redis.service.port | int | `6379` | Redis service port | +| websocket.redis.service.type | string | `"ClusterIP"` | Redis service type | +| websocket.url | string | `"redis://open-webui-redis:6379/0"` | Specifies the URL of the Redis instance for websocket communication. Template with `redis://[:@]:/` | + +---------------------------------------------- + +Autogenerated from chart metadata using [helm-docs](https://github.com/norwoodj/helm-docs/). diff --git a/charts/open-webui/README.md.gotmpl b/charts/open-webui/README.md.gotmpl new file mode 100644 index 0000000..89fee1c --- /dev/null +++ b/charts/open-webui/README.md.gotmpl @@ -0,0 +1,36 @@ +{{ template "chart.header" . }} + +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.badgesSection" . }} + +{{ template "chart.description" . }} + +{{ template "chart.homepageLine" . }} + +{{ template "chart.maintainersSection" . }} + +{{ template "chart.sourcesSection" . }} + +## Installing + +Before you can install, you need to add the `open-webui` repo to [Helm](https://helm.sh) + +```shell +helm repo add open-webui https://helm.openwebui.com/ +helm repo update +``` + +Now you can install the chart: + +```shell +helm upgrade --install open-webui open-webui/open-webui +``` + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +---------------------------------------------- + +Autogenerated from chart metadata using [helm-docs](https://github.com/norwoodj/helm-docs/). diff --git a/charts/open-webui/charts/ollama/.helmignore b/charts/open-webui/charts/ollama/.helmignore new file mode 100644 index 0000000..50dab1d --- /dev/null +++ b/charts/open-webui/charts/ollama/.helmignore @@ -0,0 +1,30 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +.drone.yml +*.tmproj +.vscode/ + +#others +.github +kind-config.yml +ci/ + diff --git a/charts/open-webui/charts/ollama/Chart.yaml b/charts/open-webui/charts/ollama/Chart.yaml new file mode 100644 index 0000000..7e3da58 --- /dev/null +++ b/charts/open-webui/charts/ollama/Chart.yaml @@ -0,0 +1,28 @@ +annotations: + artifacthub.io/category: ai-machine-learning + artifacthub.io/changes: | + - kind: changed + description: upgrade app version to 0.5.6 + links: + - name: Ollama release v0.5.6 + url: https://github.com/ollama/ollama/releases/tag/v0.5.6 +apiVersion: v2 +appVersion: 0.5.6 +description: Get up and running with large language models locally. +home: https://ollama.ai/ +icon: https://ollama.ai/public/ollama.png +keywords: +- ai +- llm +- llama +- mistral +kubeVersion: ^1.16.0-0 +maintainers: +- email: contact@otwld.com + name: OTWLD +name: ollama +sources: +- https://github.com/ollama/ollama +- https://github.com/otwld/ollama-helm +type: application +version: 1.3.0 diff --git a/charts/open-webui/charts/ollama/LICENSE b/charts/open-webui/charts/ollama/LICENSE new file mode 100644 index 0000000..01a4c80 --- /dev/null +++ b/charts/open-webui/charts/ollama/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 OTWLD + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/charts/open-webui/charts/ollama/README.md b/charts/open-webui/charts/ollama/README.md new file mode 100644 index 0000000..b6e744d --- /dev/null +++ b/charts/open-webui/charts/ollama/README.md @@ -0,0 +1,295 @@ +![otwld ollama helm chart banner](./banner.png) + +![GitHub License](https://img.shields.io/github/license/otwld/ollama-helm) +[![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/ollama-helm)](https://artifacthub.io/packages/helm/ollama-helm/ollama) +[![Build Status](https://drone.otwld.com/api/badges/otwld/ollama-helm/status.svg)](https://drone.otwld.com/otwld/ollama-helm) +[![Discord](https://img.shields.io/badge/Discord-OTWLD-blue?logo=discord&logoColor=white)](https://discord.gg/U24mpqTynB) + +[Ollama](https://ollama.ai/), get up and running with large language models, locally. + +This Community Chart is for deploying [Ollama](https://github.com/ollama/ollama). + +## Requirements + +- Kubernetes: `>= 1.16.0-0` for **CPU only** + +- Kubernetes: `>= 1.26.0-0` for **GPU** stable support (NVIDIA and AMD) + +*Not all GPUs are currently supported with ollama (especially with AMD)* + +## Deploying Ollama chart + +To install the `ollama` chart in the `ollama` namespace: + +```console +helm repo add ollama-helm https://otwld.github.io/ollama-helm/ +helm repo update +helm install ollama ollama-helm/ollama --namespace ollama +``` + +## Upgrading Ollama chart + +First please read the [release notes](https://github.com/ollama/ollama/releases) of Ollama to make sure there are no +backwards incompatible changes. + +Make adjustments to your values as needed, then run `helm upgrade`: + +```console +# -- This pulls the latest version of the ollama chart from the repo. +helm repo update +helm upgrade ollama ollama-helm/ollama --namespace ollama --values values.yaml +``` + +## Uninstalling Ollama chart + +To uninstall/delete the `ollama` deployment in the `ollama` namespace: + +```console +helm delete ollama --namespace ollama +``` + +Substitute your values if they differ from the examples. See `helm delete --help` for a full reference on `delete` +parameters and flags. + +## Interact with Ollama + +- **Ollama documentation can be found [HERE](https://github.com/ollama/ollama/tree/main/docs)** +- Interact with RESTful API: [Ollama API](https://github.com/ollama/ollama/blob/main/docs/api.md) +- Interact with official clients libraries: [ollama-js](https://github.com/ollama/ollama-js#custom-client) + and [ollama-python](https://github.com/ollama/ollama-python#custom-client) +- Interact with langchain: [langchain-js](https://github.com/ollama/ollama/blob/main/docs/tutorials/langchainjs.md) + and [langchain-python](https://github.com/ollama/ollama/blob/main/docs/tutorials/langchainpy.md) + +## Examples + +- **It's highly recommended to run an updated version of Kubernetes for deploying ollama with GPU** + +### Basic values.yaml example with GPU and two models pulled at startup + +``` +ollama: + gpu: + # -- Enable GPU integration + enabled: true + + # -- GPU type: 'nvidia' or 'amd' + type: 'nvidia' + + # -- Specify the number of GPU to 1 + number: 1 + + # -- List of models to pull at container startup + models: + pull: + - mistral + - llama2 +``` + +--- + +### Basic values.yaml example with Ingress + +``` +ollama: + models: + pull: + - llama2 + +ingress: + enabled: true + hosts: + - host: ollama.domain.lan + paths: + - path: / + pathType: Prefix +``` + +- *API is now reachable at `ollama.domain.lan`* + +## Upgrading from 0.X.X to 1.X.X + +The version 1.X.X introduces the ability to load models in memory at startup, the values have been changed. + +Please change `ollama.models` to `ollama.models.pull` to avoid errors before upgrading: + +```yaml +ollama: + models: + - mistral + - llama2 +``` + +To: + +```yaml +ollama: + models: + pull: + - mistral + - llama2 +``` + +## Helm Values + +- See [values.yaml](values.yaml) to see the Chart's default values. + +| Key | Type | Default | Description | +|--------------------------------------------|--------|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| affinity | object | `{}` | Affinity for pod assignment | +| autoscaling.enabled | bool | `false` | Enable autoscaling | +| autoscaling.maxReplicas | int | `100` | Number of maximum replicas | +| autoscaling.minReplicas | int | `1` | Number of minimum replicas | +| autoscaling.targetCPUUtilizationPercentage | int | `80` | CPU usage to target replica | +| extraArgs | list | `[]` | Additional arguments on the output Deployment definition. | +| extraEnv | list | `[]` | Additional environments variables on the output Deployment definition. For extra OLLAMA env, please refer to https://github.com/ollama/ollama/blob/main/envconfig/config.go | +| extraEnvFrom | list | `[]` | Additionl environment variables from external sources (like ConfigMap) | +| fullnameOverride | string | `""` | String to fully override template | +| hostIPC | bool | `false` | Use the host’s ipc namespace. | +| hostNetwork | bool | `false` | Use the host's network namespace. | +| hostPID | bool | `false` | Use the host’s pid namespace | +| image.pullPolicy | string | `"IfNotPresent"` | Docker pull policy | +| image.repository | string | `"ollama/ollama"` | Docker image registry | +| image.tag | string | `""` | Docker image tag, overrides the image tag whose default is the chart appVersion. | +| imagePullSecrets | list | `[]` | Docker registry secret names as an array | +| ingress.annotations | object | `{}` | Additional annotations for the Ingress resource. | +| ingress.className | string | `""` | IngressClass that will be used to implement the Ingress (Kubernetes 1.18+) | +| ingress.enabled | bool | `false` | Enable ingress controller resource | +| ingress.hosts[0].host | string | `"ollama.local"` | | +| ingress.hosts[0].paths[0].path | string | `"/"` | | +| ingress.hosts[0].paths[0].pathType | string | `"Prefix"` | | +| ingress.tls | list | `[]` | The tls configuration for hostnames to be covered with this ingress record. | +| initContainers | list | `[]` | Init containers to add to the pod | +| knative.containerConcurrency | int | `0` | Knative service container concurrency | +| knative.enabled | bool | `false` | Enable Knative integration | +| knative.idleTimeoutSeconds | int | `300` | Knative service idle timeout seconds | +| knative.responseStartTimeoutSeconds | int | `300` | Knative service response start timeout seconds | +| knative.timeoutSeconds | int | `300` | Knative service timeout seconds | +| lifecycle | object | `{}` | Lifecycle for pod assignment (override ollama.models startup pulling) | +| livenessProbe.enabled | bool | `true` | Enable livenessProbe | +| livenessProbe.failureThreshold | int | `6` | Failure threshold for livenessProbe | +| livenessProbe.initialDelaySeconds | int | `60` | Initial delay seconds for livenessProbe | +| livenessProbe.path | string | `"/"` | Request path for livenessProbe | +| livenessProbe.periodSeconds | int | `10` | Period seconds for livenessProbe | +| livenessProbe.successThreshold | int | `1` | Success threshold for livenessProbe | +| livenessProbe.timeoutSeconds | int | `5` | Timeout seconds for livenessProbe | +| nameOverride | string | `""` | String to partially override template (will maintain the release name) | +| namespaceOverride | string | `""` | String to override the namespace | +| nodeSelector | object | `{}` | Node labels for pod assignment. | +| ollama.gpu.enabled | bool | `false` | Enable GPU integration | +| ollama.gpu.mig.devices | object | `{}` | Specify the mig devices and the corresponding number | +| ollama.gpu.mig.enabled | bool | `false` | Enable multiple mig devices If enabled you will have to specify the mig devices If enabled is set to false this section is ignored | +| ollama.gpu.number | int | `1` | Specify the number of GPU If you use MIG section below then this parameter is ignored | +| ollama.gpu.nvidiaResource | string | `"nvidia.com/gpu"` | only for nvidia cards; change to (example) 'nvidia.com/mig-1g.10gb' to use MIG slice | +| ollama.gpu.type | string | `"nvidia"` | GPU type: 'nvidia' or 'amd' If 'ollama.gpu.enabled', default value is nvidia If set to 'amd', this will add 'rocm' suffix to image tag if 'image.tag' is not override This is due cause AMD and CPU/CUDA are different images | +| ollama.insecure | bool | `false` | Add insecure flag for pulling at container startup | +| ollama.models.pull | list | `[]` | List of models to pull at container startup The more you add, the longer the container will take to start if models are not present pull: - llama2 - mistral | +| ollama.models.run | list | `[]` | List of models to load in memory at container startup run: - llama2 - mistral | +| ollama.mountPath | string | `""` | Override ollama-data volume mount path, default: "/root/.ollama" | +| persistentVolume.accessModes | list | `["ReadWriteOnce"]` | Ollama server data Persistent Volume access modes Must match those of existing PV or dynamic provisioner Ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ | +| persistentVolume.annotations | object | `{}` | Ollama server data Persistent Volume annotations | +| persistentVolume.enabled | bool | `false` | Enable persistence using PVC | +| persistentVolume.existingClaim | string | `""` | If you'd like to bring your own PVC for persisting Ollama state, pass the name of the created + ready PVC here. If set, this Chart will not create the default PVC. Requires server.persistentVolume.enabled: true | +| persistentVolume.size | string | `"30Gi"` | Ollama server data Persistent Volume size | +| persistentVolume.storageClass | string | `""` | Ollama server data Persistent Volume Storage Class If defined, storageClassName: If set to "-", storageClassName: "", which disables dynamic provisioning If undefined (the default) or set to null, no storageClassName spec is set, choosing the default provisioner. (gp2 on AWS, standard on GKE, AWS & OpenStack) | +| persistentVolume.subPath | string | `""` | Subdirectory of Ollama server data Persistent Volume to mount Useful if the volume's root directory is not empty | +| persistentVolume.volumeMode | string | `""` | Ollama server data Persistent Volume Binding Mode If defined, volumeMode: If empty (the default) or set to null, no volumeBindingMode spec is set, choosing the default mode. | +| persistentVolume.volumeName | string | `""` | Pre-existing PV to attach this claim to Useful if a CSI auto-provisions a PV for you and you want to always reference the PV moving forward | +| podAnnotations | object | `{}` | Map of annotations to add to the pods | +| podLabels | object | `{}` | Map of labels to add to the pods | +| podSecurityContext | object | `{}` | Pod Security Context | +| readinessProbe.enabled | bool | `true` | Enable readinessProbe | +| readinessProbe.failureThreshold | int | `6` | Failure threshold for readinessProbe | +| readinessProbe.initialDelaySeconds | int | `30` | Initial delay seconds for readinessProbe | +| readinessProbe.path | string | `"/"` | Request path for readinessProbe | +| readinessProbe.periodSeconds | int | `5` | Period seconds for readinessProbe | +| readinessProbe.successThreshold | int | `1` | Success threshold for readinessProbe | +| readinessProbe.timeoutSeconds | int | `3` | Timeout seconds for readinessProbe | +| replicaCount | int | `1` | Number of replicas | +| resources.limits | object | `{}` | Pod limit | +| resources.requests | object | `{}` | Pod requests | +| runtimeClassName | string | `""` | Specify runtime class | +| securityContext | object | `{}` | Container Security Context | +| service.annotations | object | `{}` | Annotations to add to the service | +| service.loadBalancerIP | string | `nil` | Load Balancer IP address | +| service.nodePort | int | `31434` | Service node port when service type is 'NodePort' | +| service.port | int | `11434` | Service port | +| service.type | string | `"ClusterIP"` | Service type | +| serviceAccount.annotations | object | `{}` | Annotations to add to the service account | +| serviceAccount.automount | bool | `true` | Automatically mount a ServiceAccount's API credentials? | +| serviceAccount.create | bool | `true` | Specifies whether a service account should be created | +| serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the fullname template | +| tolerations | list | `[]` | Tolerations for pod assignment | +| topologySpreadConstraints | object | `{}` | Topology Spread Constraints for pod assignment | +| updateStrategy.type | string | `"Recreate"` | Deployment strategy can be "Recreate" or "RollingUpdate". Default is Recreate | +| volumeMounts | list | `[]` | Additional volumeMounts on the output Deployment definition. | +| volumes | list | `[]` | Additional volumes on the output Deployment definition. | + +---------------------------------------------- + +## Core team + + + + + + +
+ Jean Baptiste Detroyes
     Jean Baptiste Detroyes     
+
+ + +
+
+ Jean Baptiste Detroyes
     Nathan Tréhout     
+
+ + + +
+
+ +## Support + +- For questions, suggestions, and discussion about Ollama please refer to + the [Ollama issue page](https://github.com/ollama/ollama/issues) +- For questions, suggestions, and discussion about this chart please + visit [Ollama-Helm issue page](https://github.com/otwld/ollama-helm/issues) or join + our [OTWLD Discord](https://discord.gg/U24mpqTynB) diff --git a/charts/open-webui/charts/ollama/artifacthub-repo.yml b/charts/open-webui/charts/ollama/artifacthub-repo.yml new file mode 100644 index 0000000..63d1dd1 --- /dev/null +++ b/charts/open-webui/charts/ollama/artifacthub-repo.yml @@ -0,0 +1,15 @@ +# Artifact Hub repository metadata file +# +# Some settings like the verified publisher flag or the ignored packages won't +# be applied until the next time the repository is processed. Please keep in +# mind that the repository won't be processed if it has not changed since the +# last time it was processed. Depending on the repository kind, this is checked +# in a different way. For Helm http based repositories, we consider it has +# changed if the `index.yaml` file changes. For git based repositories, it does +# when the hash of the last commit in the branch you set up changes. This does +# NOT apply to ownership claim operations, which are processed immediately. +# +repositoryID: 2ccfd0bd-c123-483e-96f6-eb446fa850ac +owners: + - name: Outworld + email: tech@outworld.fr \ No newline at end of file diff --git a/charts/open-webui/charts/ollama/banner.png b/charts/open-webui/charts/ollama/banner.png new file mode 100644 index 0000000000000000000000000000000000000000..72e6f57a6f9e8e5021fb6762a8deb08be642e323 GIT binary patch literal 134943 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%0AiisSDajaen-&ghWalN2_bq2~AtxbZc-60==4 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 literal 0 HcmV?d00001 diff --git a/charts/open-webui/charts/ollama/templates/NOTES.txt b/charts/open-webui/charts/ollama/templates/NOTES.txt new file mode 100644 index 0000000..4f4889d --- /dev/null +++ b/charts/open-webui/charts/ollama/templates/NOTES.txt @@ -0,0 +1,25 @@ +1. Get the application URL by running these commands: +{{- if .Values.knative.enabled }} + export KSERVICE_URL=$(kubectl get ksvc --namespace {{ .Release.Namespace }} {{ include "ollama.fullname" . }} -o jsonpath={.status.url}) + echo "Visit $KSERVICE_URL to use your application" +{{- else if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "ollama.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "ollama.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "ollama.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "ollama.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/charts/open-webui/charts/ollama/templates/_helpers.tpl b/charts/open-webui/charts/ollama/templates/_helpers.tpl new file mode 100644 index 0000000..3a4c4b0 --- /dev/null +++ b/charts/open-webui/charts/ollama/templates/_helpers.tpl @@ -0,0 +1,91 @@ +{{/* +Allow the release namespace to be overridden for multi-namespace deployments in combined charts +*/}} +{{- define "ollama.namespace" -}} + {{- if .Values.namespaceOverride -}} + {{- .Values.namespaceOverride -}} + {{- else -}} + {{- .Release.Namespace -}} + {{- end -}} +{{- end -}} + +{{/* +Expand the name of the chart. +*/}} +{{- define "ollama.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 "ollama.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 "ollama.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "ollama.labels" -}} +helm.sh/chart: {{ include "ollama.chart" . }} +{{ include "ollama.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "ollama.selectorLabels" -}} +app.kubernetes.io/name: {{ include "ollama.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "ollama.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "ollama.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Create the pull model list +*/}} +{{- define "ollama.modelPullList" -}} +{{- with .Values.ollama.models.pull -}} +{{- . | uniq | join " " -}} +{{- end -}} +{{- end -}} + +{{/* +Create the run model list +*/}} +{{- define "ollama.modelRunList" -}} +{{- with .Values.ollama.models.run -}} +{{- . | uniq | join " " -}} +{{- end -}} +{{- end -}} \ No newline at end of file diff --git a/charts/open-webui/charts/ollama/templates/deployment.yaml b/charts/open-webui/charts/ollama/templates/deployment.yaml new file mode 100644 index 0000000..c4ff392 --- /dev/null +++ b/charts/open-webui/charts/ollama/templates/deployment.yaml @@ -0,0 +1,213 @@ +--- +{{- if not .Values.knative.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "ollama.fullname" . }} + namespace: {{ include "ollama.namespace" . }} + labels: + {{- include "ollama.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + {{- if or .Values.updateStrategy.type .Values.updateStrategy.rollingUpdate }} + strategy: {{ .Values.updateStrategy | toYaml | nindent 4 }} + {{- end }} + selector: + matchLabels: + {{- include "ollama.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "ollama.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- if .Values.hostIPC }} + hostIPC: {{ .Values.hostIPC }} + {{- end }} + {{- if .Values.hostPID }} + hostPID: {{ .Values.hostPID }} + {{- end }} + {{- if .Values.hostNetwork }} + hostNetwork: {{ .Values.hostNetwork }} + {{- end }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "ollama.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + {{- if .Values.runtimeClassName }} + runtimeClassName: {{ .Values.runtimeClassName | quote }} + {{- end }} + {{- with .Values.initContainers }} + initContainers: + {{- tpl (toYaml . ) $ | nindent 8 }} + {{- end }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default (ternary (printf "%s-rocm" .Chart.AppVersion) (.Chart.AppVersion) (and (.Values.ollama.gpu.enabled) (eq .Values.ollama.gpu.type "amd"))) }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + env: + {{- if and .Values.ollama.gpu.enabled (or (eq .Values.ollama.gpu.type "nvidia") (not .Values.ollama.gpu.type))}} + - name: PATH + value: /usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + {{- end}} + {{- with .Values.extraEnv }} + {{- toYaml . | nindent 12 }} + {{- end }} + envFrom: + {{- with .Values.extraEnvFrom }} + {{- toYaml . | nindent 12 }} + {{- end }} + args: + {{- with .Values.extraArgs }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if .Values.resources }} + resources: + {{- $limits := default dict .Values.resources.limits }} + {{- if .Values.ollama.gpu.enabled }} + # If gpu is enabled, it can either be a NVIDIA card or a AMD card + {{- if or (eq .Values.ollama.gpu.type "nvidia") (not .Values.ollama.gpu.type) }} + # NVIDIA is assumed by default if no value is set and GPU is enabled + # NVIDIA cards can have mig enabled (i.e., the card is sliced into parts + # Therefore, the first case is no migs enabled + {{- if or (not .Values.ollama.gpu.mig) (not .Values.ollama.gpu.mig.enabled ) }} + {{- $gpuLimit := dict (.Values.ollama.gpu.nvidiaResource | default "nvidia.com/gpu") (.Values.ollama.gpu.number | default 1) }} + {{- $limits = merge $limits $gpuLimit }} + # Second case is mig is enabled + {{- else if or (.Values.ollama.gpu.mig.enabled) }} + # Initialize empty dictionary + {{- $migDevices := dict -}} + # Loop over the entries in the mig devices + {{- range $key, $value := .Values.ollama.gpu.mig.devices }} + {{- $migKey := printf "nvidia.com/mig-%s" $key -}} + {{- $migDevices = merge $migDevices (dict $migKey $value) -}} + {{- end }} + {{- $limits = merge $limits $migDevices}} + {{- end }} + {{- end }} + {{- if eq .Values.ollama.gpu.type "amd" }} + {{- $gpuLimit := dict "amd.com/gpu" (.Values.ollama.gpu.number | default 1) }} + {{- $limits = merge $limits $gpuLimit }} + {{- end }} + {{- end }} + {{- $ressources := deepCopy (dict "limits" $limits) | mergeOverwrite .Values.resources }} + {{- toYaml $ressources | nindent 12 }} + {{- end}} + volumeMounts: + - name: ollama-data + mountPath: {{ .Values.ollama.mountPath | default "/root/.ollama" }} + {{- if .Values.persistentVolume.subPath }} + subPath: {{ .Values.persistentVolume.subPath }} + {{- end }} + {{- with .Values.volumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if .Values.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: {{ .Values.livenessProbe.path }} + port: http + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.livenessProbe.failureThreshold }} + {{- end }} + {{- if .Values.readinessProbe.enabled }} + readinessProbe: + httpGet: + path: {{ .Values.readinessProbe.path }} + port: http + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + {{- end }} + {{- with .Values.lifecycle}} + lifecycle: + {{- toYaml . | nindent 12 }} + {{- else }} + {{- if or .Values.ollama.models.pull .Values.ollama.models.run }} + lifecycle: + postStart: + exec: + command: + - /bin/sh + - -c + - | + while ! /bin/ollama ps > /dev/null 2>&1; do + sleep 5 + done + {{- if .Values.ollama.models.pull }} + echo "{{ include "ollama.modelPullList" . }}" | xargs -n1 /bin/ollama pull {{ternary "--insecure" "" .Values.ollama.insecure | toString }} + {{- end }} + {{- if .Values.ollama.models.run }} + echo "{{ include "ollama.modelRunList" . }}" | xargs -n1 /bin/ollama run + {{- end }} + {{- end }} + {{- end }} + volumes: + - name: ollama-data + {{- if .Values.persistentVolume.enabled }} + persistentVolumeClaim: + claimName: {{ .Values.persistentVolume.existingClaim | default (printf "%s" (include "ollama.fullname" .)) }} + {{- else }} + emptyDir: { } + {{- end }} + {{- with .Values.volumes }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if or .Values.ollama.gpu.enabled .Values.tolerations }} + tolerations: + {{- if and .Values.ollama.gpu.enabled (and + ( or (eq .Values.ollama.gpu.type "nvidia") (not .Values.ollama.gpu.type)) + ( or (not .Values.ollama.gpu.mig) (not .Values.ollama.gpu.mig.enabled)) + ) }} + - key: "{{(.Values.ollama.gpu.nvidiaResource | default "nvidia.com/gpu")}}" + operator: Exists + effect: NoSchedule + {{- else if and .Values.ollama.gpu.enabled (and + ( or (eq .Values.ollama.gpu.type "nvidia") (not .Values.ollama.gpu.type)) + (( .Values.ollama.gpu.mig.enabled)) + ) }} + {{- range $key, $value := .Values.ollama.gpu.mig.devices }} + - key: nvidia.com/mig-{{ $key }} + operator: Exists + effect: NoSchedule + {{- end }} + {{- end }} + {{- with .Values.tolerations }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/open-webui/charts/ollama/templates/hpa.yaml b/charts/open-webui/charts/ollama/templates/hpa.yaml new file mode 100644 index 0000000..b249330 --- /dev/null +++ b/charts/open-webui/charts/ollama/templates/hpa.yaml @@ -0,0 +1,34 @@ +--- +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "ollama.fullname" . }} + namespace: {{ include "ollama.namespace" . }} + labels: + {{- include "ollama.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "ollama.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/charts/open-webui/charts/ollama/templates/ingress.yaml b/charts/open-webui/charts/ollama/templates/ingress.yaml new file mode 100644 index 0000000..859fc3f --- /dev/null +++ b/charts/open-webui/charts/ollama/templates/ingress.yaml @@ -0,0 +1,63 @@ +--- +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "ollama.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + namespace: {{ include "ollama.namespace" . }} + labels: + {{- include "ollama.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + 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 }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/open-webui/charts/ollama/templates/knative/service.yaml b/charts/open-webui/charts/ollama/templates/knative/service.yaml new file mode 100644 index 0000000..cad2429 --- /dev/null +++ b/charts/open-webui/charts/ollama/templates/knative/service.yaml @@ -0,0 +1,156 @@ +--- +{{- if .Values.knative.enabled }} +apiVersion: serving.knative.dev/v1 +kind: Service +metadata: + name: {{ include "ollama.fullname" . }} + namespace: {{ include "ollama.namespace" . }} + labels: + {{- include "ollama.labels" . | nindent 4 }} +spec: + template: + spec: + containerConcurrency: {{ .Values.knative.containerConcurrency }} + timeoutSeconds: {{ .Values.knative.timeoutSeconds }} + responseStartTimeoutSeconds: {{ .Values.knative.responseStartTimeoutSeconds }} + idleTimeoutSeconds: {{ .Values.knative.idleTimeoutSeconds }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "ollama.serviceAccountName" . }} + {{- if .Values.runtimeClassName }} + runtimeClassName: {{ .Values.runtimeClassName | quote }} + {{- end }} + {{- with .Values.initContainers }} + initContainers: + {{- tpl (toYaml . ) $ | nindent 8 }} + {{- end }} + containers: + - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default (ternary (printf "%s-rocm" .Chart.AppVersion) (.Chart.AppVersion) (and (.Values.ollama.gpu.enabled) (eq .Values.ollama.gpu.type "amd"))) }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + ports: + - containerPort: {{ .Values.service.port }} + env: + {{- if and .Values.ollama.gpu.enabled (or (eq .Values.ollama.gpu.type "nvidia") (not .Values.ollama.gpu.type))}} + - name: PATH + value: /usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + {{- end}} + {{- with .Values.extraEnv }} + {{- toYaml . | nindent 12 }} + {{- end }} + envFrom: + {{- with .Values.extraEnvFrom }} + {{- toYaml . | nindent 12 }} + {{- end }} + args: + {{- with .Values.extraArgs }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if .Values.resources }} + resources: + {{- $limits := default dict .Values.resources.limits }} + {{- if .Values.ollama.gpu.enabled }} + {{- if or (eq .Values.ollama.gpu.type "nvidia") (not .Values.ollama.gpu.type) }} + {{- $gpuLimit := dict (.Values.ollama.gpu.nvidiaResource | default "nvidia.com/gpu") (.Values.ollama.gpu.number | default 1) }} + {{- $limits = merge $limits $gpuLimit }} + {{- end }} + {{- if eq .Values.ollama.gpu.type "amd" }} + {{- $gpuLimit := dict "amd.com/gpu" (.Values.ollama.gpu.number | default 1) }} + {{- $limits = merge $limits $gpuLimit }} + {{- end }} + {{- end }} + {{- $ressources := deepCopy (dict "limits" $limits) | mergeOverwrite .Values.resources }} + {{- toYaml $ressources | nindent 12 }} + {{- end}} + volumeMounts: + - name: ollama-data + mountPath: {{ .Values.ollama.mountPath | default "/root/.ollama" }} + {{- if .Values.persistentVolume.subPath }} + subPath: {{ .Values.persistentVolume.subPath }} + {{- end }} + {{- with .Values.volumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if .Values.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: {{ .Values.livenessProbe.path }} + port: http + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.livenessProbe.failureThreshold }} + {{- end }} + {{- if .Values.readinessProbe.enabled }} + readinessProbe: + httpGet: + path: {{ .Values.readinessProbe.path }} + port: http + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + {{- end }} + {{- with .Values.lifecycle}} + lifecycle: + {{- toYaml . | nindent 12 }} + {{- else }} + {{- if or .Values.ollama.models.pull .Values.ollama.models.run }} + lifecycle: + postStart: + exec: + command: + - /bin/sh + - -c + - | + while ! /bin/ollama ps > /dev/null 2>&1; do + sleep 5 + done + {{- if .Values.ollama.models.pull }} + echo "{{ include "ollama.modelPullList" . }}" | xargs -n1 /bin/ollama pull {{ternary "--insecure" "" .Values.ollama.insecure | toString }} + {{- end }} + {{- if .Values.ollama.models.run }} + echo "{{ include "ollama.modelRunList" . }}" | xargs -n1 /bin/ollama run + {{- end }} + {{- end }} + {{- end }} + volumes: + - name: ollama-data + {{- if .Values.persistentVolume.enabled }} + persistentVolumeClaim: + claimName: {{ .Values.persistentVolume.existingClaim | default (printf "%s" (include "ollama.fullname" .)) }} + {{- else }} + emptyDir: { } + {{- end }} + {{- with .Values.volumes }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if or .Values.ollama.gpu.enabled .Values.tolerations }} + tolerations: + {{- if and .Values.ollama.gpu.enabled (or (eq .Values.ollama.gpu.type "nvidia") (not .Values.ollama.gpu.type)) }} + - key: "{{(.Values.ollama.gpu.nvidiaResource | default "nvidia.com/gpu")}}" + operator: Exists + effect: NoSchedule + {{- end }} + {{- with .Values.tolerations }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/open-webui/charts/ollama/templates/pvc.yaml b/charts/open-webui/charts/ollama/templates/pvc.yaml new file mode 100644 index 0000000..6708cb9 --- /dev/null +++ b/charts/open-webui/charts/ollama/templates/pvc.yaml @@ -0,0 +1,35 @@ +--- +{{- if .Values.persistentVolume.enabled -}} +{{- if not .Values.persistentVolume.existingClaim -}} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + {{- if .Values.persistentVolume.annotations }} + annotations: +{{ toYaml .Values.persistentVolume.annotations | indent 4 }} + {{- end }} + labels: + {{- include "ollama.labels" . | nindent 4 }} + name: {{ template "ollama.fullname" . }} + namespace: {{ include "ollama.namespace" . }} +spec: + accessModes: +{{ toYaml .Values.persistentVolume.accessModes | indent 4 }} +{{- if .Values.persistentVolume.storageClass }} +{{- if (eq "-" .Values.persistentVolume.storageClass) }} + storageClassName: "" +{{- else }} + storageClassName: "{{ .Values.persistentVolume.storageClass }}" +{{- end }} +{{- end }} +{{- if .Values.persistentVolume.volumeMode }} + volumeMode: "{{ .Values.persistentVolume.volumeMode }}" +{{- end }} +{{- if .Values.persistentVolume.volumeName }} + volumeName: "{{ .Values.persistentVolume.volumeName }}" +{{- end }} + resources: + requests: + storage: "{{ .Values.persistentVolume.size }}" +{{- end -}} +{{- end -}} diff --git a/charts/open-webui/charts/ollama/templates/service.yaml b/charts/open-webui/charts/ollama/templates/service.yaml new file mode 100644 index 0000000..6540fac --- /dev/null +++ b/charts/open-webui/charts/ollama/templates/service.yaml @@ -0,0 +1,29 @@ +--- +{{- if not .Values.knative.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "ollama.fullname" . }} + namespace: {{ include "ollama.namespace" . }} + labels: + {{- include "ollama.labels" . | nindent 4 }} + {{- 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 + {{- if contains "NodePort" .Values.service.type }} + nodePort: {{ .Values.service.nodePort }} + {{- end }} +{{- if .Values.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.service.loadBalancerIP | quote }} +{{- end }} + selector: + {{- include "ollama.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/charts/open-webui/charts/ollama/templates/serviceaccount.yaml b/charts/open-webui/charts/ollama/templates/serviceaccount.yaml new file mode 100644 index 0000000..25ac575 --- /dev/null +++ b/charts/open-webui/charts/ollama/templates/serviceaccount.yaml @@ -0,0 +1,15 @@ +{{- if .Values.serviceAccount.create -}} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "ollama.serviceAccountName" . }} + namespace: {{ include "ollama.namespace" . }} + labels: + {{- include "ollama.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/charts/open-webui/charts/ollama/templates/tests/test-connection.yaml b/charts/open-webui/charts/ollama/templates/tests/test-connection.yaml new file mode 100644 index 0000000..5ccb42c --- /dev/null +++ b/charts/open-webui/charts/ollama/templates/tests/test-connection.yaml @@ -0,0 +1,17 @@ +--- +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "ollama.fullname" . }}-test-connection" + namespace: {{ include "ollama.namespace" . }} + labels: + {{- include "ollama.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "ollama.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/charts/open-webui/charts/ollama/values-test.yaml b/charts/open-webui/charts/ollama/values-test.yaml new file mode 100644 index 0000000..916686a --- /dev/null +++ b/charts/open-webui/charts/ollama/values-test.yaml @@ -0,0 +1,311 @@ +# Default values for ollama-helm. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +# -- Number of replicas +replicaCount: 1 + +# Knative configuration +knative: + # -- Enable Knative integration + enabled: false + # -- Knative service container concurrency + containerConcurrency: 0 + # -- Knative service timeout seconds + timeoutSeconds: 300 + # -- Knative service response start timeout seconds + responseStartTimeoutSeconds: 300 + # -- Knative service idle timeout seconds + idleTimeoutSeconds: 300 + +# Docker image +image: + # -- Docker image registry + repository: ollama/ollama + + # -- Docker pull policy + pullPolicy: IfNotPresent + + # -- Docker image tag, overrides the image tag whose default is the chart appVersion. + tag: "" + +# -- Docker registry secret names as an array +imagePullSecrets: [] + +# -- String to partially override template (will maintain the release name) +nameOverride: "" + +# -- String to fully override template +fullnameOverride: "" + +# Ollama parameters +ollama: + gpu: + # -- Enable GPU integration + enabled: false + + # -- GPU type: 'nvidia' or 'amd' + # If 'ollama.gpu.enabled', default value is nvidia + # If set to 'amd', this will add 'rocm' suffix to image tag if 'image.tag' is not override + # This is due cause AMD and CPU/CUDA are different images + type: 'nvidia' + + # -- Specify the number of GPU + # If you use MIG section below then this parameter is ignored + number: 1 + + # -- only for nvidia cards; change to (example) 'nvidia.com/mig-1g.10gb' to use MIG slice + nvidiaResource: "nvidia.com/gpu" + # nvidiaResource: "nvidia.com/mig-1g.10gb" # example + # If you want to use more than one NVIDIA MIG you can use the following syntax (then nvidiaResource is ignored and only the configuration in the following MIG section is used) + + mig: + # -- Enable multiple mig devices + # If enabled you will have to specify the mig devices + # If enabled is set to false this section is ignored + enabled: false + + # -- Specify the mig devices and the corresponding number + devices: {} + # 1g.10gb: 1 + # 3g.40gb: 1 + + models: + - llama2 + + + # -- Add insecure flag for pulling at container startup + insecure: false + + # -- Override ollama-data volume mount path, default: "/root/.ollama" + mountPath: "" + +# Service account +# ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ +serviceAccount: + # -- Specifies whether a service account should be created + create: true + + # -- Automatically mount a ServiceAccount's API credentials? + automount: true + + # -- 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: "" + +# -- Map of annotations to add to the pods +podAnnotations: {} + +# -- Map of labels to add to the pods +podLabels: {} + +# -- Pod Security Context +podSecurityContext: {} + # fsGroup: 2000 + +# -- Container Security Context +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +# -- Specify runtime class +runtimeClassName: "" + +# Configure Service +service: + + # -- Service type + type: ClusterIP + + # -- Service port + port: 11434 + + # -- Service node port when service type is 'NodePort' + nodePort: 31434 + + # -- Load Balancer IP address + loadBalancerIP: + + # -- Annotations to add to the service + annotations: {} + +# Configure extra options for liveness probe +# ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes +livenessProbe: + # -- Enable livenessProbe + enabled: true + + # -- Request path for livenessProbe + path: / + + # -- Initial delay seconds for livenessProbe + initialDelaySeconds: 60 + + # -- Period seconds for livenessProbe + periodSeconds: 10 + + # -- Timeout seconds for livenessProbe + timeoutSeconds: 5 + + # -- Failure threshold for livenessProbe + failureThreshold: 6 + + # -- Success threshold for livenessProbe + successThreshold: 1 + +# Configure extra options for readiness probe +# ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes +readinessProbe: + # -- Enable readinessProbe + enabled: true + + # -- Request path for readinessProbe + path: / + + # -- Initial delay seconds for readinessProbe + initialDelaySeconds: 30 + + # -- Period seconds for readinessProbe + periodSeconds: 5 + + # -- Timeout seconds for readinessProbe + timeoutSeconds: 3 + + # -- Failure threshold for readinessProbe + failureThreshold: 6 + + # -- Success threshold for readinessProbe + successThreshold: 1 + +# Configure autoscaling +autoscaling: + # -- Enable autoscaling + enabled: false + + # -- Number of minimum replicas + minReplicas: 1 + + # -- Number of maximum replicas + maxReplicas: 100 + + # -- CPU usage to target replica + targetCPUUtilizationPercentage: 80 + + # -- targetMemoryUtilizationPercentage: 80 + +# -- Additional volumes on the output Deployment definition. +volumes: [] +# -- - name: foo +# secret: +# secretName: mysecret +# optional: false + +# -- Additional volumeMounts on the output Deployment definition. +volumeMounts: [] +# -- - name: foo +# mountPath: "/etc/foo" +# readOnly: true + +# -- Additional arguments on the output Deployment definition. +extraArgs: [] + +# -- Additional environments variables on the output Deployment definition. +# For extra OLLAMA env, please refer to https://github.com/ollama/ollama/blob/main/envconfig/config.go +extraEnv: [] +# - name: OLLAMA_DEBUG +# value: "1" + +# -- Additionl environment variables from external sources (like ConfigMap) +extraEnvFrom: [] +# - configMapRef: +# name: my-env-configmap + +# Enable persistence using Persistent Volume Claims +# ref: https://kubernetes.io/docs/concepts/storage/persistent-volumes/ +persistentVolume: + # -- Enable persistence using PVC + enabled: false + + # -- Ollama server data Persistent Volume access modes + # Must match those of existing PV or dynamic provisioner + # Ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ + accessModes: + - ReadWriteOnce + + # -- Ollama server data Persistent Volume annotations + annotations: {} + + # -- If you'd like to bring your own PVC for persisting Ollama state, pass the name of the + # created + ready PVC here. If set, this Chart will not create the default PVC. + # Requires server.persistentVolume.enabled: true + existingClaim: "" + + # -- Ollama server data Persistent Volume size + size: 30Gi + + # -- Ollama server data Persistent Volume Storage Class + # If defined, storageClassName: + # If set to "-", storageClassName: "", which disables dynamic provisioning + # If undefined (the default) or set to null, no storageClassName spec is + # set, choosing the default provisioner. (gp2 on AWS, standard on + # GKE, AWS & OpenStack) + storageClass: "" + + # -- Ollama server data Persistent Volume Binding Mode + # If defined, volumeMode: + # If empty (the default) or set to null, no volumeBindingMode spec is + # set, choosing the default mode. + volumeMode: "" + + # -- Subdirectory of Ollama server data Persistent Volume to mount + # Useful if the volume's root directory is not empty + subPath: "" + + # -- Pre-existing PV to attach this claim to + # Useful if a CSI auto-provisions a PV for you and you want to always + # reference the PV moving forward + volumeName: "" + +# -- Node labels for pod assignment. +nodeSelector: {} + +# -- Tolerations for pod assignment +tolerations: [] + +# -- Affinity for pod assignment +affinity: {} + +# -- Lifecycle for pod assignment (override ollama.models startup pull/run) +lifecycle: {} + +# How to replace existing pods +updateStrategy: + # -- Deployment strategy can be "Recreate" or "RollingUpdate". Default is Recreate + type: "Recreate" + +# -- Topology Spread Constraints for pod assignment +topologySpreadConstraints: {} + +# -- Init containers to add to the pod +initContainers: [] +# - name: startup-tool +# image: alpine:3 +# command: [sh, -c] +# args: +# - echo init + +# -- Use the host’s ipc namespace. +hostIPC: false + +# -- Use the host’s pid namespace +hostPID: false + +# -- Use the host's network namespace. +hostNetwork: false \ No newline at end of file diff --git a/charts/open-webui/charts/ollama/values.yaml b/charts/open-webui/charts/ollama/values.yaml new file mode 100644 index 0000000..dd41ed4 --- /dev/null +++ b/charts/open-webui/charts/ollama/values.yaml @@ -0,0 +1,370 @@ +# Default values for ollama-helm. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +# -- Number of replicas +replicaCount: 1 + +# Knative configuration +knative: + # -- Enable Knative integration + enabled: false + # -- Knative service container concurrency + containerConcurrency: 0 + # -- Knative service timeout seconds + timeoutSeconds: 300 + # -- Knative service response start timeout seconds + responseStartTimeoutSeconds: 300 + # -- Knative service idle timeout seconds + idleTimeoutSeconds: 300 + +# Docker image +image: + # -- Docker image registry + repository: ollama/ollama + + # -- Docker pull policy + pullPolicy: IfNotPresent + + # -- Docker image tag, overrides the image tag whose default is the chart appVersion. + tag: "" + +# -- Docker registry secret names as an array +imagePullSecrets: [] + +# -- String to partially override template (will maintain the release name) +nameOverride: "" + +# -- String to fully override template +fullnameOverride: "" + +# -- String to fully override namespace +namespaceOverride: "" + +# Ollama parameters +ollama: + gpu: + # -- Enable GPU integration + enabled: false + + # -- GPU type: 'nvidia' or 'amd' + # If 'ollama.gpu.enabled', default value is nvidia + # If set to 'amd', this will add 'rocm' suffix to image tag if 'image.tag' is not override + # This is due cause AMD and CPU/CUDA are different images + type: 'nvidia' + + # -- Specify the number of GPU + # If you use MIG section below then this parameter is ignored + number: 1 + + # -- only for nvidia cards; change to (example) 'nvidia.com/mig-1g.10gb' to use MIG slice + nvidiaResource: "nvidia.com/gpu" + # nvidiaResource: "nvidia.com/mig-1g.10gb" # example + # If you want to use more than one NVIDIA MIG you can use the following syntax (then nvidiaResource is ignored and only the configuration in the following MIG section is used) + + mig: + # -- Enable multiple mig devices + # If enabled you will have to specify the mig devices + # If enabled is set to false this section is ignored + enabled: false + + # -- Specify the mig devices and the corresponding number + devices: {} + # 1g.10gb: 1 + # 3g.40gb: 1 + + models: + # -- List of models to pull at container startup + # The more you add, the longer the container will take to start if models are not present + # pull: + # - llama2 + # - mistral + pull: [] + + # -- List of models to load in memory at container startup + # run: + # - llama2 + # - mistral + run: [] + + # -- Add insecure flag for pulling at container startup + insecure: false + + # -- Override ollama-data volume mount path, default: "/root/.ollama" + mountPath: "" + +# Service account +# ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ +serviceAccount: + # -- Specifies whether a service account should be created + create: true + + # -- Automatically mount a ServiceAccount's API credentials? + automount: true + + # -- 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: "" + +# -- Map of annotations to add to the pods +podAnnotations: {} + +# -- Map of labels to add to the pods +podLabels: {} + +# -- Pod Security Context +podSecurityContext: {} + # fsGroup: 2000 + +# -- Container Security Context +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +# -- Specify runtime class +runtimeClassName: "" + +# Configure Service +service: + + # -- Service type + type: ClusterIP + + # -- Service port + port: 11434 + + # -- Service node port when service type is 'NodePort' + nodePort: 31434 + + # -- Load Balancer IP address + loadBalancerIP: + + # -- Annotations to add to the service + annotations: {} + +# Configure the ingress resource that allows you to access the +ingress: + # -- Enable ingress controller resource + enabled: false + + # -- IngressClass that will be used to implement the Ingress (Kubernetes 1.18+) + className: "" + + # -- Additional annotations for the Ingress resource. + annotations: {} + # kubernetes.io/ingress.class: traefik + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + + # The list of hostnames to be covered with this ingress record. + hosts: + - host: ollama.local + paths: + - path: / + pathType: Prefix + + # -- The tls configuration for hostnames to be covered with this ingress record. + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +# Configure resource requests and limits +# ref: http://kubernetes.io/docs/user-guide/compute-resources/ +resources: + # -- Pod requests + requests: {} + # Memory request + # memory: 4096Mi + + # CPU request + # cpu: 2000m + + # -- Pod limit + limits: {} + # Memory limit + # memory: 8192Mi + + # CPU limit + # cpu: 4000m + +# Configure extra options for liveness probe +# ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes +livenessProbe: + # -- Enable livenessProbe + enabled: true + + # -- Request path for livenessProbe + path: / + + # -- Initial delay seconds for livenessProbe + initialDelaySeconds: 60 + + # -- Period seconds for livenessProbe + periodSeconds: 10 + + # -- Timeout seconds for livenessProbe + timeoutSeconds: 5 + + # -- Failure threshold for livenessProbe + failureThreshold: 6 + + # -- Success threshold for livenessProbe + successThreshold: 1 + +# Configure extra options for readiness probe +# ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes +readinessProbe: + # -- Enable readinessProbe + enabled: true + + # -- Request path for readinessProbe + path: / + + # -- Initial delay seconds for readinessProbe + initialDelaySeconds: 30 + + # -- Period seconds for readinessProbe + periodSeconds: 5 + + # -- Timeout seconds for readinessProbe + timeoutSeconds: 3 + + # -- Failure threshold for readinessProbe + failureThreshold: 6 + + # -- Success threshold for readinessProbe + successThreshold: 1 + +# Configure autoscaling +autoscaling: + # -- Enable autoscaling + enabled: false + + # -- Number of minimum replicas + minReplicas: 1 + + # -- Number of maximum replicas + maxReplicas: 100 + + # -- CPU usage to target replica + targetCPUUtilizationPercentage: 80 + + # -- targetMemoryUtilizationPercentage: 80 + +# -- Additional volumes on the output Deployment definition. +volumes: [] +# -- - name: foo +# secret: +# secretName: mysecret +# optional: false + +# -- Additional volumeMounts on the output Deployment definition. +volumeMounts: [] +# -- - name: foo +# mountPath: "/etc/foo" +# readOnly: true + +# -- Additional arguments on the output Deployment definition. +extraArgs: [] + +# -- Additional environments variables on the output Deployment definition. +# For extra OLLAMA env, please refer to https://github.com/ollama/ollama/blob/main/envconfig/config.go +extraEnv: [] +# - name: OLLAMA_DEBUG +# value: "1" + +# -- Additionl environment variables from external sources (like ConfigMap) +extraEnvFrom: [] +# - configMapRef: +# name: my-env-configmap + +# Enable persistence using Persistent Volume Claims +# ref: https://kubernetes.io/docs/concepts/storage/persistent-volumes/ +persistentVolume: + # -- Enable persistence using PVC + enabled: false + + # -- Ollama server data Persistent Volume access modes + # Must match those of existing PV or dynamic provisioner + # Ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ + accessModes: + - ReadWriteOnce + + # -- Ollama server data Persistent Volume annotations + annotations: {} + + # -- If you'd like to bring your own PVC for persisting Ollama state, pass the name of the + # created + ready PVC here. If set, this Chart will not create the default PVC. + # Requires server.persistentVolume.enabled: true + existingClaim: "" + + # -- Ollama server data Persistent Volume size + size: 30Gi + + # -- Ollama server data Persistent Volume Storage Class + # If defined, storageClassName: + # If set to "-", storageClassName: "", which disables dynamic provisioning + # If undefined (the default) or set to null, no storageClassName spec is + # set, choosing the default provisioner. (gp2 on AWS, standard on + # GKE, AWS & OpenStack) + storageClass: "" + + # -- Ollama server data Persistent Volume Binding Mode + # If defined, volumeMode: + # If empty (the default) or set to null, no volumeBindingMode spec is + # set, choosing the default mode. + volumeMode: "" + + # -- Subdirectory of Ollama server data Persistent Volume to mount + # Useful if the volume's root directory is not empty + subPath: "" + + # -- Pre-existing PV to attach this claim to + # Useful if a CSI auto-provisions a PV for you and you want to always + # reference the PV moving forward + volumeName: "" + +# -- Node labels for pod assignment. +nodeSelector: {} + +# -- Tolerations for pod assignment +tolerations: [] + +# -- Affinity for pod assignment +affinity: {} + +# -- Lifecycle for pod assignment (override ollama.models startup pull/run) +lifecycle: {} + +# How to replace existing pods +updateStrategy: + # -- Deployment strategy can be "Recreate" or "RollingUpdate". Default is Recreate + type: "Recreate" + +# -- Topology Spread Constraints for pod assignment +topologySpreadConstraints: {} + +# -- Init containers to add to the pod +initContainers: [] +# - name: startup-tool +# image: alpine:3 +# command: [sh, -c] +# args: +# - echo init + +# -- Use the host’s ipc namespace. +hostIPC: false + +# -- Use the host’s pid namespace +hostPID: false + +# -- Use the host's network namespace. +hostNetwork: false diff --git a/charts/open-webui/charts/pipelines/.helmignore b/charts/open-webui/charts/pipelines/.helmignore new file mode 100644 index 0000000..1e540ba --- /dev/null +++ b/charts/open-webui/charts/pipelines/.helmignore @@ -0,0 +1,24 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +.drone.yml +*.tmproj +.vscode/ \ No newline at end of file diff --git a/charts/open-webui/charts/pipelines/Chart.yaml b/charts/open-webui/charts/pipelines/Chart.yaml new file mode 100644 index 0000000..593a890 --- /dev/null +++ b/charts/open-webui/charts/pipelines/Chart.yaml @@ -0,0 +1,16 @@ +annotations: + licenses: MIT +apiVersion: v2 +appVersion: alpha +description: 'Pipelines: UI-Agnostic OpenAI API Plugin Framework' +home: https://github.com/open-webui/pipelines +icon: https://github.com/open-webui/pipelines/raw/main/header.png +keywords: +- llm +- chat +- web-ui +name: pipelines +sources: +- https://github.com/open-webui/helm-charts +- https://github.com/open-webui/pipelines/pkgs/container/pipelines +version: 0.0.6 diff --git a/charts/open-webui/charts/pipelines/README.md b/charts/open-webui/charts/pipelines/README.md new file mode 100644 index 0000000..6054ecf --- /dev/null +++ b/charts/open-webui/charts/pipelines/README.md @@ -0,0 +1,71 @@ +# pipelines + +![Version: 0.0.4](https://img.shields.io/badge/Version-0.0.4-informational?style=flat-square) ![AppVersion: alpha](https://img.shields.io/badge/AppVersion-alpha-informational?style=flat-square) + +Pipelines: UI-Agnostic OpenAI API Plugin Framework + +**Homepage:** + +## Source Code + +* +* + +## Installing + +Before you can install, you need to add the `open-webui` repo to [Helm](https://helm.sh) + +```shell +helm repo add open-webui https://helm.openwebui.com/ +helm repo update +``` + +Now you can install the chart: + +```shell +helm upgrade --install open-webui open-webui/pipelines +``` + +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| affinity | object | `{}` | Affinity for pod assignment | +| annotations | object | `{}` | | +| clusterDomain | string | `"cluster.local"` | Value of cluster domain | +| extraEnvVars | list | `[{"name":"PIPELINES_URLS","value":"https://github.com/open-webui/pipelines/blob/main/examples/filters/detoxify_filter_pipeline.py"}]` | Additional environments variables on the output Deployment definition. These are used to pull initial Pipeline files, and help configure Pipelines with required values (e.g. Langfuse API keys) | +| extraEnvVars[0] | object | `{"name":"PIPELINES_URLS","value":"https://github.com/open-webui/pipelines/blob/main/examples/filters/detoxify_filter_pipeline.py"}` | Example pipeline to pull and load on deployment startup, see current pipelines here: https://github.com/open-webui/pipelines/blob/main/examples | +| image.pullPolicy | string | `"Always"` | | +| image.repository | string | `"ghcr.io/open-webui/pipelines"` | | +| image.tag | string | `"main"` | | +| imagePullSecrets | list | `[]` | Configure imagePullSecrets to use private registry ref: | +| ingress.annotations | object | `{}` | Use appropriate annotations for your Ingress controller, e.g., for NGINX: nginx.ingress.kubernetes.io/rewrite-target: / | +| ingress.class | string | `""` | | +| ingress.enabled | bool | `true` | | +| ingress.existingSecret | string | `""` | | +| ingress.host | string | `""` | | +| ingress.tls | bool | `false` | | +| nameOverride | string | `""` | | +| nodeSelector | object | `{}` | Node labels for pod assignment. | +| persistence.accessModes | list | `["ReadWriteOnce"]` | If using multiple replicas, you must update accessModes to ReadWriteMany | +| persistence.annotations | object | `{}` | | +| persistence.enabled | bool | `true` | | +| persistence.existingClaim | string | `""` | | +| persistence.selector | object | `{}` | | +| persistence.size | string | `"2Gi"` | | +| persistence.storageClass | string | `""` | | +| podAnnotations | object | `{}` | | +| replicaCount | int | `1` | | +| resources | object | `{}` | | +| service.annotations | object | `{}` | | +| service.containerPort | int | `9099` | | +| service.labels | object | `{}` | | +| service.loadBalancerClass | string | `""` | | +| service.nodePort | string | `""` | | +| service.port | int | `9099` | | +| service.type | string | `"ClusterIP"` | | +| tolerations | list | `[]` | Tolerations for pod assignment | + +---------------------------------------------- + +Autogenerated from chart metadata using [helm-docs](https://github.com/norwoodj/helm-docs/). diff --git a/charts/open-webui/charts/pipelines/README.md.gotmpl b/charts/open-webui/charts/pipelines/README.md.gotmpl new file mode 100644 index 0000000..88d437d --- /dev/null +++ b/charts/open-webui/charts/pipelines/README.md.gotmpl @@ -0,0 +1,36 @@ +{{ template "chart.header" . }} + +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.badgesSection" . }} + +{{ template "chart.description" . }} + +{{ template "chart.homepageLine" . }} + +{{ template "chart.maintainersSection" . }} + +{{ template "chart.sourcesSection" . }} + +## Installing + +Before you can install, you need to add the `open-webui` repo to [Helm](https://helm.sh) + +```shell +helm repo add open-webui https://helm.openwebui.com/ +helm repo update +``` + +Now you can install the chart: + +```shell +helm upgrade --install open-webui open-webui/pipelines +``` + +{{ template "chart.requirementsSection" . }} + +{{ template "chart.valuesSection" . }} + +---------------------------------------------- + +Autogenerated from chart metadata using [helm-docs](https://github.com/norwoodj/helm-docs/). diff --git a/charts/open-webui/charts/pipelines/templates/_helpers.tpl b/charts/open-webui/charts/pipelines/templates/_helpers.tpl new file mode 100644 index 0000000..a33f23a --- /dev/null +++ b/charts/open-webui/charts/pipelines/templates/_helpers.tpl @@ -0,0 +1,65 @@ +{{/* +Allow the release namespace to be overridden for multi-namespace deployments in combined charts +*/}} +{{- define "pipelines.namespace" -}} + {{- if .Values.namespaceOverride -}} + {{- .Values.namespaceOverride -}} + {{- else -}} + {{- .Release.Namespace -}} + {{- end -}} +{{- end -}} + +{{/* +Set the name of the Pipelines resources +*/}} +{{- define "pipelines.name" -}} +{{- default .Release.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end -}} + +{{/* +Create the chart name and version for the chart label +*/}} +{{- define "chart.name" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create the base labels to include on chart resources +*/}} +{{- define "base.labels" -}} +helm.sh/chart: {{ include "chart.name" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Create selector labels to include on all resources +*/}} +{{- define "base.selectorLabels" -}} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{/* +Create selector labels to include on all Pipelines resources +*/}} +{{- define "pipelines.selectorLabels" -}} +{{ include "base.selectorLabels" . }} +app.kubernetes.io/component: {{ .Chart.Name }} +{{- end }} + +{{/* +Create labels to include on all Pipelines resources +*/}} +{{- define "pipelines.labels" -}} +{{ include "base.labels" . }} +{{ include "pipelines.selectorLabels" . }} +{{- end }} + +{{/* +Create the default port to use on the service if none is defined in values +*/}} +{{- define "pipelines.servicePort" -}} +{{- .Values.service.port | default 9099 }} +{{- end }} diff --git a/charts/open-webui/charts/pipelines/templates/deployment.yaml b/charts/open-webui/charts/pipelines/templates/deployment.yaml new file mode 100644 index 0000000..1edfa39 --- /dev/null +++ b/charts/open-webui/charts/pipelines/templates/deployment.yaml @@ -0,0 +1,79 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "pipelines.name" . }} + namespace: {{ include "pipelines.namespace" . }} + labels: + {{- include "pipelines.labels" . | nindent 4 }} + {{- with .Values.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "pipelines.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "pipelines.labels" . | nindent 8 }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + enableServiceLinks: false + automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken | default false }} + {{- if .Values.serviceAccount.enable }} + serviceAccountName: {{ .Values.serviceAccount.name | default (include "pipelines.name" .) }} + {{- end }} + containers: + - name: {{ .Chart.Name }} + {{- with .Values.image }} + image: {{ .repository }}:{{ .tag | default $.Chart.AppVersion }} + imagePullPolicy: {{ .pullPolicy }} + {{- end }} + ports: + - name: http + containerPort: {{ .Values.service.containerPort }} + {{- with .Values.resources }} + resources: {{- toYaml . | nindent 10 }} + {{- end }} + volumeMounts: + - name: data + mountPath: /app/pipelines + env: + {{- if .Values.extraEnvVars }} + {{- toYaml .Values.extraEnvVars | nindent 8 }} + {{- end }} + tty: true + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + {{- if and .Values.persistence.enabled .Values.persistence.existingClaim }} + - name: data + persistentVolumeClaim: + claimName: {{ .Values.persistence.existingClaim }} + {{- else if not .Values.persistence.enabled }} + - name: data + emptyDir: {} + {{- else if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} + - name: data + persistentVolumeClaim: + claimName: {{ include "pipelines.name" . }} + {{- end }} diff --git a/charts/open-webui/charts/pipelines/templates/pvc.yaml b/charts/open-webui/charts/pipelines/templates/pvc.yaml new file mode 100644 index 0000000..c16f12a --- /dev/null +++ b/charts/open-webui/charts/pipelines/templates/pvc.yaml @@ -0,0 +1,28 @@ +{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "pipelines.name" . }} + namespace: {{ include "pipelines.namespace" . }} + labels: + {{- include "pipelines.selectorLabels" . | nindent 4 }} + {{- with .Values.persistence.annotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} +spec: + accessModes: + {{- range .Values.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size }} + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass }} + {{- end }} + {{- with .Values.persistence.selector }} + selector: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/open-webui/charts/pipelines/templates/service-account.yaml b/charts/open-webui/charts/pipelines/templates/service-account.yaml new file mode 100644 index 0000000..4da3f3e --- /dev/null +++ b/charts/open-webui/charts/pipelines/templates/service-account.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.enable }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Values.serviceAccount.name | default (include "pipelines.name" .) }} + namespace: {{ include "pipelines.namespace" . }} + labels: + {{- include "pipelines.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/open-webui/charts/pipelines/templates/service.yaml b/charts/open-webui/charts/pipelines/templates/service.yaml new file mode 100644 index 0000000..746fa35 --- /dev/null +++ b/charts/open-webui/charts/pipelines/templates/service.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "pipelines.name" . }} + namespace: {{ include "pipelines.namespace" . }} + labels: + {{- include "pipelines.labels" . | nindent 4 }} + {{- with .Values.service.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + selector: + {{- include "pipelines.selectorLabels" . | nindent 4 }} + type: {{ .Values.service.type | default "ClusterIP" }} + ports: + - protocol: TCP + name: http + port: {{ .Values.service.port }} + targetPort: http + {{- if .Values.service.nodePort }} + nodePort: {{ .Values.service.nodePort | int }} + {{- end }} + {{- if .Values.service.loadBalancerClass }} + loadBalancerClass: {{ .Values.service.loadBalancerClass | quote }} + {{- end }} + \ No newline at end of file diff --git a/charts/open-webui/charts/pipelines/values.yaml b/charts/open-webui/charts/pipelines/values.yaml new file mode 100644 index 0000000..eb21501 --- /dev/null +++ b/charts/open-webui/charts/pipelines/values.yaml @@ -0,0 +1,88 @@ +nameOverride: "" +namespaceOverride: "" + +# -- Value of cluster domain +clusterDomain: cluster.local + +annotations: {} +podAnnotations: {} +replicaCount: 1 +image: + repository: ghcr.io/open-webui/pipelines + tag: main + pullPolicy: Always + +# -- Configure imagePullSecrets to use private registry +# ref: +imagePullSecrets: [] +# imagePullSecrets: +# - name: myRegistryKeySecretName + +resources: {} +ingress: + enabled: true + class: "" + # -- Use appropriate annotations for your Ingress controller, e.g., for NGINX: + # nginx.ingress.kubernetes.io/rewrite-target: / + annotations: {} + host: "" + tls: false + existingSecret: "" +persistence: + enabled: true + size: 2Gi + existingClaim: "" + # -- If using multiple replicas, you must update accessModes to ReadWriteMany + accessModes: + - ReadWriteOnce + storageClass: "" + selector: {} + annotations: {} + +serviceAccount: + enable: true + automountServiceAccountToken: false + +# -- Node labels for pod assignment. +nodeSelector: {} + +# -- Tolerations for pod assignment +tolerations: [] + +# -- Affinity for pod assignment +affinity: {} + +service: + type: ClusterIP + annotations: {} + port: 9099 + containerPort: 9099 + nodePort: "" + labels: {} + loadBalancerClass: "" + +# -- Additional environments variables on the output Deployment definition. These are used to pull initial Pipeline files, and help configure Pipelines with required values (e.g. Langfuse API keys) +extraEnvVars: + # -- Example pipeline to pull and load on deployment startup, see current pipelines here: https://github.com/open-webui/pipelines/blob/main/examples + - name: PIPELINES_URLS + value: "https://github.com/open-webui/pipelines/blob/main/examples/filters/detoxify_filter_pipeline.py" + # - name: PIPELINES_API_KEY + # valueFrom: + # secretKeyRef: + # name: pipelines-keys + # key: pipelines-api-key + # -- Langfuse example, including values used in Langfuse filter to connect + # - name: PIPELINES_URLS + # value: "https://github.com/open-webui/pipelines/blob/main/examples/filters/langfuse_filter_pipeline.py" + # - name: LANGFUSE_PUBLIC_KEY + # valueFrom: + # secretKeyRef: + # name: langfuse-keys + # key: public-key + # - name: LANGFUSE_SECRET_KEY + # valueFrom: + # secretKeyRef: + # name: langfuse-keys + # key: secret-key + # - name: LANGFUSE_HOST + # value: https://us.cloud.langfuse.com diff --git a/charts/open-webui/charts/redis/.helmignore b/charts/open-webui/charts/redis/.helmignore new file mode 100644 index 0000000..207983f --- /dev/null +++ b/charts/open-webui/charts/redis/.helmignore @@ -0,0 +1,25 @@ +# 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 +# img folder +img/ +# Changelog +CHANGELOG.md diff --git a/charts/open-webui/charts/redis/Chart.lock b/charts/open-webui/charts/redis/Chart.lock new file mode 100644 index 0000000..c65fdcc --- /dev/null +++ b/charts/open-webui/charts/redis/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: common + repository: oci://registry-1.docker.io/bitnamicharts + version: 2.29.0 +digest: sha256:c0ac22dea12ebe35613a1fbde2d9ffcf913c40dc688139f10914db942f2dab13 +generated: "2025-01-07T17:38:19.921368699Z" diff --git a/charts/open-webui/charts/redis/Chart.yaml b/charts/open-webui/charts/redis/Chart.yaml new file mode 100644 index 0000000..864d11c --- /dev/null +++ b/charts/open-webui/charts/redis/Chart.yaml @@ -0,0 +1,38 @@ +annotations: + category: Database + images: | + - name: kubectl + image: docker.io/bitnami/kubectl:1.32.0-debian-12-r0 + - name: os-shell + image: docker.io/bitnami/os-shell:12-debian-12-r34 + - name: redis + image: docker.io/bitnami/redis:7.4.2-debian-12-r0 + - name: redis-exporter + image: docker.io/bitnami/redis-exporter:1.67.0-debian-12-r0 + - name: redis-sentinel + image: docker.io/bitnami/redis-sentinel:7.4.2-debian-12-r0 + licenses: Apache-2.0 +apiVersion: v2 +appVersion: 7.4.2 +dependencies: +- name: common + repository: oci://registry-1.docker.io/bitnamicharts + tags: + - bitnami-common + version: 2.x.x +description: Redis(R) is an open source, advanced key-value store. It is often referred + to as a data structure server since keys can contain strings, hashes, lists, sets + and sorted sets. +home: https://bitnami.com +icon: https://bitnami.com/assets/stacks/redis/img/redis-stack-220x234.png +keywords: +- redis +- keyvalue +- database +maintainers: +- name: Broadcom, Inc. All Rights Reserved. + url: https://github.com/bitnami/charts +name: redis +sources: +- https://github.com/bitnami/charts/tree/main/bitnami/redis +version: 20.6.3 diff --git a/charts/open-webui/charts/redis/README.md b/charts/open-webui/charts/redis/README.md new file mode 100644 index 0000000..8391596 --- /dev/null +++ b/charts/open-webui/charts/redis/README.md @@ -0,0 +1,1321 @@ + + +# Bitnami package for Redis(R) + +Redis(R) is an open source, advanced key-value store. It is often referred to as a data structure server since keys can contain strings, hashes, lists, sets and sorted sets. + +[Overview of Redis®](http://redis.io) + +Disclaimer: Redis is a registered trademark of Redis Ltd. Any rights therein are reserved to Redis Ltd. Any use by Bitnami is for referential purposes only and does not indicate any sponsorship, endorsement, or affiliation between Redis Ltd. + +## TL;DR + +```console +helm install my-release oci://registry-1.docker.io/bitnamicharts/redis +``` + +Looking to use Redis® in production? Try [VMware Tanzu Application Catalog](https://bitnami.com/enterprise), the commercial edition of the Bitnami catalog. + +## Introduction + +This chart bootstraps a [Redis®](https://github.com/bitnami/containers/tree/main/bitnami/redis) deployment on a [Kubernetes](https://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +Bitnami charts can be used with [Kubeapps](https://kubeapps.dev/) for deployment and management of Helm Charts in clusters. + +### Choose between Redis® Helm Chart and Redis® Cluster Helm Chart + +You can choose any of the two Redis® Helm charts for deploying a Redis® cluster. + +1. [Redis® Helm Chart](https://github.com/bitnami/charts/tree/main/bitnami/redis) will deploy a master-replica cluster, with the [option](https://github.com/bitnami/charts/tree/main/bitnami/redis#redis-sentinel-configuration-parameters) of enabling using Redis® Sentinel. +2. [Redis® Cluster Helm Chart](https://github.com/bitnami/charts/tree/main/bitnami/redis-cluster) will deploy a Redis® Cluster topology with sharding. + +The main features of each chart are the following: + +| Redis® | Redis® Cluster | +|--------------------------------------------------------|------------------------------------------------------------------------| +| Supports multiple databases | Supports only one database. Better if you have a big dataset | +| Single write point (single master) | Multiple write points (multiple masters) | +| ![Redis® Topology](img/redis-topology.png) | ![Redis® Cluster Topology](img/redis-cluster-topology.png) | + +## Prerequisites + +- Kubernetes 1.23+ +- Helm 3.8.0+ +- PV provisioner support in the underlying infrastructure + +## Installing the Chart + +To install the chart with the release name `my-release`: + +```console +helm install my-release oci://REGISTRY_NAME/REPOSITORY_NAME/redis +``` + +> Note: You need to substitute the placeholders `REGISTRY_NAME` and `REPOSITORY_NAME` with a reference to your Helm chart registry and repository. For example, in the case of Bitnami, you need to use `REGISTRY_NAME=registry-1.docker.io` and `REPOSITORY_NAME=bitnamicharts`. + +The command deploys Redis® on the Kubernetes cluster in the default configuration. The [Parameters](#parameters) section lists the parameters that can be configured during installation. + +> **Tip**: List all releases using `helm list` + +## Configuration and installation details + +### Resource requests and limits + +Bitnami charts allow setting resource requests and limits for all containers inside the chart deployment. These are inside the `resources` value (check parameter table). Setting requests is essential for production workloads and these should be adapted to your specific use case. + +To make this process easier, the chart contains the `resourcesPreset` values, which automatically sets the `resources` section according to different presets. Check these presets in [the bitnami/common chart](https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15). However, in production workloads using `resourcesPreset` is discouraged as it may not fully adapt to your specific needs. Find more information on container resource management in the [official Kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/). + +### Prometheus metrics + +This chart can be integrated with Prometheus by setting `metrics.enabled` to `true`. This will deploy a sidecar container with [redis_exporter](https://github.com/oliver006/redis_exporter) in all pods and a `metrics` service, which can be configured under the `metrics.service` section. This `metrics` service will have the necessary annotations to be automatically scraped by Prometheus. + +#### Prometheus requirements + +It is necessary to have a working installation of Prometheus or Prometheus Operator for the integration to work. Install the [Bitnami Prometheus helm chart](https://github.com/bitnami/charts/tree/main/bitnami/prometheus) or the [Bitnami Kube Prometheus helm chart](https://github.com/bitnami/charts/tree/main/bitnami/kube-prometheus) to easily have a working Prometheus in your cluster. + +#### Integration with Prometheus Operator + +The chart can deploy `ServiceMonitor` objects for integration with Prometheus Operator installations. To do so, set the value `metrics.serviceMonitor.enabled=true`. Ensure that the Prometheus Operator `CustomResourceDefinitions` are installed in the cluster or it will fail with the following error: + +```text +no matches for kind "ServiceMonitor" in version "monitoring.coreos.com/v1" +``` + +Install the [Bitnami Kube Prometheus helm chart](https://github.com/bitnami/charts/tree/main/bitnami/kube-prometheus) for having the necessary CRDs and the Prometheus Operator. + +### [Rolling VS Immutable tags](https://techdocs.broadcom.com/us/en/vmware-tanzu/application-catalog/tanzu-application-catalog/services/tac-doc/apps-tutorials-understand-rolling-tags-containers-index.html) + +It is strongly recommended to use immutable tags in a production environment. This ensures your deployment does not change automatically if the same tag is updated with a different image. + +Bitnami will release a new chart updating its containers if a new version of the main container, significant changes, or critical vulnerabilities exist. + +### Use a different Redis® version + +To modify the application version used in this chart, specify a different version of the image using the `image.tag` parameter and/or a different repository using the `image.repository` parameter. + +### Bootstrapping with an External Cluster + +This chart is equipped with the ability to bring online a set of Pods that connect to an existing Redis deployment that lies outside of Kubernetes. This effectively creates a hybrid Redis Deployment where both Pods in Kubernetes and Instances such as Virtual Machines can partake in a single Redis Deployment. This is helpful in situations where one may be migrating Redis from Virtual Machines into Kubernetes, for example. To take advantage of this, use the following as an example configuration: + +```yaml +replica: + externalMaster: + enabled: true + host: external-redis-0.internal +sentinel: + externalMaster: + enabled: true + host: external-redis-0.internal +``` + +:warning: This is currently limited to clusters in which Sentinel and Redis run on the same node! :warning: + +Please also note that the external sentinel must be listening on port `26379`, and this is currently not configurable. + +Once the Kubernetes Redis Deployment is online and confirmed to be working with the existing cluster, the configuration can then be removed and the cluster will remain connected. + +### External DNS + +This chart is equipped to allow leveraging the ExternalDNS project. Doing so will enable ExternalDNS to publish the FQDN for each instance, in the format of `..`. +Example, when using the following configuration: + +```yaml +useExternalDNS: + enabled: true + suffix: prod.example.org + additionalAnnotations: + ttl: 10 +``` + +On a cluster where the name of the Helm release is `a`, the hostname of a Pod is generated as: `a-redis-node-0.a-redis.prod.example.org`. The IP of that FQDN will match that of the associated Pod. This modifies the following parameters of the Redis/Sentinel configuration using this new FQDN: + +- `replica-announce-ip` +- `known-sentinel` +- `known-replica` +- `announce-ip` + +:warning: This requires a working installation of `external-dns` to be fully functional. :warning: + +See the [official ExternalDNS documentation](https://github.com/kubernetes-sigs/external-dns) for additional configuration options. + +### Cluster topologies + +#### Default: Master-Replicas + +When installing the chart with `architecture=replication`, it will deploy a Redis® master StatefulSet and a Redis® replicas StatefulSet. The replicas will be read-replicas of the master. Two services will be exposed: + +- Redis® Master service: Points to the master, where read-write operations can be performed +- Redis® Replicas service: Points to the replicas, where only read operations are allowed by default. + +In case the master crashes, the replicas will wait until the master node is respawned again by the Kubernetes Controller Manager. + +#### Standalone + +When installing the chart with `architecture=standalone`, it will deploy a standalone Redis® StatefulSet. A single service will be exposed: + +- Redis® Master service: Points to the master, where read-write operations can be performed + +#### Master-Replicas with Sentinel + +When installing the chart with `architecture=replication` and `sentinel.enabled=true`, it will deploy a Redis® master StatefulSet (only one master allowed) and a Redis® replicas StatefulSet. In this case, the pods will contain an extra container with Redis® Sentinel. This container will form a cluster of Redis® Sentinel nodes, which will promote a new master in case the actual one fails. + +On graceful termination of the Redis® master pod, a failover of the master is initiated to promote a new master. The Redis® Sentinel container in this pod will wait for the failover to occur before terminating. If `sentinel.redisShutdownWaitFailover=true` is set (the default), the Redis® container will wait for the failover as well before terminating. This increases availability for reads during failover, but may cause stale reads until all clients have switched to the new master. + +In addition to this, only one service is exposed: + +- Redis® service: Exposes port 6379 for Redis® read-only operations and port 26379 for accessing Redis® Sentinel. + +For read-only operations, access the service using port 6379. For write operations, it's necessary to access the Redis® Sentinel cluster and query the current master using the command below (using redis-cli or similar): + +```console +SENTINEL get-master-addr-by-name +``` + +This command will return the address of the current master, which can be accessed from inside the cluster. + +In case the current master crashes, the Sentinel containers will elect a new master node. + +`master.count` greater than `1` is not designed for use when `sentinel.enabled=true`. + +### Multiple masters (experimental) + +When `master.count` is greater than `1`, special care must be taken to create a consistent setup. + +An example of use case is the creation of a redundant set of standalone masters or master-replicas per Kubernetes node where you must ensure: + +- No more than `1` master can be deployed per Kubernetes node +- Replicas and writers can only see the single master of their own Kubernetes node + +One way of achieving this is by setting `master.service.internalTrafficPolicy=Local` in combination with a `master.affinity.podAntiAffinity` spec to never schedule more than one master per Kubernetes node. + +It's recommended to only change `master.count` if you know what you are doing. +`master.count` greater than `1` is not designed for use when `sentinel.enabled=true`. + +### Update credentials + +The Bitnami Redis chart, when upgrading, reuses the secret previously rendered by the chart or the one specified in `auth.existingSecret`. To update credentials, use one of the following: + +- Run `helm upgrade` specifying a new password in `auth.password` +- Run `helm upgrade` specifying a new secret in `auth.existingSecret` + +### Using a password file + +To use a password file for Redis® you need to create a secret containing the password and then deploy the chart using that secret. Follow these instructions: + +- Create the secret with the password. It is important that the file with the password must be called `redis-password`. + +```console +kubectl create secret generic redis-password-secret --from-file=redis-password.yaml +``` + +- Deploy the Helm Chart using the secret name as parameter: + +```text +usePassword=true +usePasswordFiles=true +existingSecret=redis-password-secret +sentinels.enabled=true +metrics.enabled=true +``` + +### Securing traffic using TLS + +TLS support can be enabled in the chart by specifying the `tls.` parameters while creating a release. The following parameters should be configured to properly enable the TLS support in the cluster: + +- `tls.enabled`: Enable TLS support. Defaults to `false` +- `tls.existingSecret`: Name of the secret that contains the certificates. No defaults. +- `tls.certFilename`: Certificate filename. No defaults. +- `tls.certKeyFilename`: Certificate key filename. No defaults. +- `tls.certCAFilename`: CA Certificate filename. No defaults. + +For example: + +First, create the secret with the certificates files: + +```console +kubectl create secret generic certificates-tls-secret --from-file=./cert.pem --from-file=./cert.key --from-file=./ca.pem +``` + +Then, use the following parameters: + +```console +tls.enabled="true" +tls.existingSecret="certificates-tls-secret" +tls.certFilename="cert.pem" +tls.certKeyFilename="cert.key" +tls.certCAFilename="ca.pem" +``` + +### Metrics + +The chart optionally can start a metrics exporter for [prometheus](https://prometheus.io). The metrics endpoint (port 9121) is exposed in the service. Metrics can be scraped from within the cluster using something similar as the described in the [example Prometheus scrape configuration](https://github.com/prometheus/prometheus/blob/master/documentation/examples/prometheus-kubernetes.yml). If metrics are to be scraped from outside the cluster, the Kubernetes API proxy can be utilized to access the endpoint. + +If you have enabled TLS by specifying `tls.enabled=true` you also need to specify TLS option to the metrics exporter. You can do that via `metrics.extraArgs`. You can find the metrics exporter CLI flags for TLS [here](https://github.com/oliver006/redis_exporter#command-line-flags). For example: + +You can either specify `metrics.extraArgs.skip-tls-verification=true` to skip TLS verification or providing the following values under `metrics.extraArgs` for TLS client authentication: + +```console +tls-client-key-file +tls-client-cert-file +tls-ca-cert-file +``` + +### Deploy a custom metrics script in the sidecar + +A custom Lua script can be added to the `redis-exporter` sidecar by way of the `metrics.extraArgs.script` parameter. The pathname of the script must exist on the container, or the `redis_exporter` process (and therefore the whole pod) will refuse to start. The script can be provided to the sidecar containers via the `metrics.extraVolumes` and `metrics.extraVolumeMounts` parameters: + +```yaml +metrics: + extraVolumeMounts: + - name: '{{ printf "%s-metrics-script-file" (include "common.names.fullname" .) }}' + mountPath: '{{ printf "/mnt/%s/" (include "common.names.name" .) }}' + readOnly: true + extraVolumes: + - name: '{{ printf "%s-metrics-script-file" (include "common.names.fullname" .) }}' + configMap: + name: '{{ printf "%s-metrics-script" (include "common.names.fullname" .) }}' + extraArgs: + script: '{{ printf "/mnt/%s/my_custom_metrics.lua" (include "common.names.name" .) }}' +``` + +Then deploy the script into the correct location via `extraDeploy`: + +```yaml +extraDeploy: + - apiVersion: v1 + kind: ConfigMap + metadata: + name: '{{ printf "%s-metrics-script" (include "common.names.fullname" .) }}' + data: + my_custom_metrics.lua: | + -- LUA SCRIPT CODE HERE, e.g., + return {'bitnami_makes_the_best_charts', '1'} +``` + +### Host Kernel Settings + +Redis® may require some changes in the kernel of the host machine to work as expected, in particular increasing the `somaxconn` value and disabling transparent huge pages. To do so, you can set up a privileged `initContainer` with the `sysctlImage` config values, for example: + +```yaml +sysctlImage: + enabled: true + mountHostSys: true + command: + - /bin/sh + - -c + - |- + install_packages procps + sysctl -w net.core.somaxconn=10000 + echo never > /host-sys/kernel/mm/transparent_hugepage/enabled +``` + +Alternatively, for Kubernetes 1.12+ you can set `securityContext.sysctls` which will configure `sysctls` for master and slave pods. Example: + +```yaml +securityContext: + sysctls: + - name: net.core.somaxconn + value: "10000" +``` + +Note that this will not disable transparent huge tables. + +### Backup and restore + +To backup and restore Redis deployments on Kubernetes, you will need to create a snapshot of the data in the source cluster, and later restore it in a new cluster with the new parameters. Follow the instructions below: + +#### Step 1: Backup the deployment + +- Connect to one of the nodes and start the Redis CLI tool. Then, run the commands below: + + ```text + $ kubectl exec -it my-release-master-0 bash + $ redis-cli + 127.0.0.1:6379> auth your_current_redis_password + OK + 127.0.0.1:6379> save + OK + ``` + +- Copy the dump file from the Redis node: + + ```console + kubectl cp my-release-master-0:/data/dump.rdb dump.rdb -c redis + ``` + +#### Step 2: Restore the data on the destination cluster + +To restore the data in a new cluster, you will need to create a PVC and then upload the *dump.rdb* file to the new volume. + +Follow the following steps: + +- In the [*values.yaml*](https://github.com/bitnami/charts/blob/main/bitnami/redis/values.yaml) file set the *appendonly* parameter to *no*. You can skip this step if it is already configured as *no* + + ```yaml + commonConfiguration: |- + # Enable AOF https://redis.io/topics/persistence#append-only-file + appendonly no + # Disable RDB persistence, AOF persistence already enabled. + save "" + ``` + + > *Note that the `Enable AOF` comment belongs to the original config file and what you're actually doing is disabling it. This change will only be neccessary for the temporal cluster you're creating to upload the dump.* + +- Start the new cluster to create the PVCs. Use the command below as an example: + + ```console + helm install new-redis -f values.yaml . --set cluster.enabled=true --set cluster.slaveCount=3 + ``` + +- Now that the PVC were created, stop it and copy the *dump.rdp* file on the persisted data by using a helping pod. + + ```text + $ helm delete new-redis + + $ kubectl run --generator=run-pod/v1 -i --rm --tty volpod --overrides=' + { + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "name": "redisvolpod" + }, + "spec": { + "containers": [{ + "command": [ + "tail", + "-f", + "/dev/null" + ], + "image": "bitnami/minideb", + "name": "mycontainer", + "volumeMounts": [{ + "mountPath": "/mnt", + "name": "redisdata" + }] + }], + "restartPolicy": "Never", + "volumes": [{ + "name": "redisdata", + "persistentVolumeClaim": { + "claimName": "redis-data-new-redis-master-0" + } + }] + } + }' --image="bitnami/minideb" + + $ kubectl cp dump.rdb redisvolpod:/mnt/dump.rdb + $ kubectl delete pod volpod + ``` + +- Restart the cluster: + + > **INFO:** The *appendonly* parameter can be safely restored to your desired value. + + ```console + helm install new-redis -f values.yaml . --set cluster.enabled=true --set cluster.slaveCount=3 + ``` + +### NetworkPolicy + +To enable network policy for Redis®, install [a networking plugin that implements the Kubernetes NetworkPolicy spec](https://kubernetes.io/docs/tasks/administer-cluster/declare-network-policy#before-you-begin), and set `networkPolicy.enabled` to `true`. + +With NetworkPolicy enabled, only pods with the generated client label will be able to connect to Redis. This label will be displayed in the output after a successful install. + +With `networkPolicy.ingressNSMatchLabels` pods from other namespaces can connect to Redis. Set `networkPolicy.ingressNSPodMatchLabels` to match pod labels in matched namespace. For example, for a namespace labeled `redis=external` and pods in that namespace labeled `redis-client=true` the fields should be set: + +```yaml +networkPolicy: + enabled: true + ingressNSMatchLabels: + redis: external + ingressNSPodMatchLabels: + redis-client: true +``` + +#### Setting Pod's affinity + +This chart allows you to set your custom affinity using the `XXX.affinity` parameter(s). Find more information about Pod's affinity in the [Kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity). + +As an alternative, you can use of the preset configurations for pod affinity, pod anti-affinity, and node affinity available at the [bitnami/common](https://github.com/bitnami/charts/tree/main/bitnami/common#affinities) chart. To do so, set the `XXX.podAffinityPreset`, `XXX.podAntiAffinityPreset`, or `XXX.nodeAffinityPreset` parameters. + +## Persistence + +By default, the chart mounts a [Persistent Volume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) at the `/data` path. The volume is created using dynamic volume provisioning. If a Persistent Volume Claim already exists, specify it during installation. + +### Existing PersistentVolumeClaim + +1. Create the PersistentVolume +2. Create the PersistentVolumeClaim +3. Install the chart + +```console +helm install my-release --set master.persistence.existingClaim=PVC_NAME oci://REGISTRY_NAME/REPOSITORY_NAME/redis +``` + +> Note: You need to substitute the placeholders `REGISTRY_NAME` and `REPOSITORY_NAME` with a reference to your Helm chart registry and repository. For example, in the case of Bitnami, you need to use `REGISTRY_NAME=registry-1.docker.io` and `REPOSITORY_NAME=bitnamicharts`. + +## Parameters + +### Global parameters + +| Name | Description | Value | +| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| `global.imageRegistry` | Global Docker image registry | `""` | +| `global.imagePullSecrets` | Global Docker registry secret names as an array | `[]` | +| `global.defaultStorageClass` | Global default StorageClass for Persistent Volume(s) | `""` | +| `global.storageClass` | DEPRECATED: use global.defaultStorageClass instead | `""` | +| `global.redis.password` | Global Redis® password (overrides `auth.password`) | `""` | +| `global.security.allowInsecureImages` | Allows skipping image verification | `false` | +| `global.compatibility.openshift.adaptSecurityContext` | Adapt the securityContext sections of the deployment to make them compatible with Openshift restricted-v2 SCC: remove runAsUser, runAsGroup and fsGroup and let the platform use their allowed default IDs. Possible values: auto (apply if the detected running cluster is Openshift), force (perform the adaptation always), disabled (do not perform adaptation) | `auto` | + +### Common parameters + +| Name | Description | Value | +| ------------------------- | -------------------------------------------------------------------------------------------------------------- | --------------- | +| `kubeVersion` | Override Kubernetes version | `""` | +| `nameOverride` | String to partially override common.names.fullname | `""` | +| `fullnameOverride` | String to fully override common.names.fullname | `""` | +| `namespaceOverride` | String to fully override common.names.namespace | `""` | +| `commonLabels` | Labels to add to all deployed objects | `{}` | +| `commonAnnotations` | Annotations to add to all deployed objects | `{}` | +| `secretAnnotations` | Annotations to add to secret | `{}` | +| `clusterDomain` | Kubernetes cluster domain name | `cluster.local` | +| `extraDeploy` | Array of extra objects to deploy with the release | `[]` | +| `useHostnames` | Use hostnames internally when announcing replication. If false, the hostname will be resolved to an IP address | `true` | +| `nameResolutionThreshold` | Failure threshold for internal hostnames resolution | `5` | +| `nameResolutionTimeout` | Timeout seconds between probes for internal hostnames resolution | `5` | +| `diagnosticMode.enabled` | Enable diagnostic mode (all probes will be disabled and the command will be overridden) | `false` | +| `diagnosticMode.command` | Command to override all containers in the deployment | `["sleep"]` | +| `diagnosticMode.args` | Args to override all containers in the deployment | `["infinity"]` | + +### Redis® Image parameters + +| Name | Description | Value | +| ------------------- | ---------------------------------------------------------------------------------------------------------- | ----------------------- | +| `image.registry` | Redis® image registry | `REGISTRY_NAME` | +| `image.repository` | Redis® image repository | `REPOSITORY_NAME/redis` | +| `image.digest` | Redis® image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `image.pullPolicy` | Redis® image pull policy | `IfNotPresent` | +| `image.pullSecrets` | Redis® image pull secrets | `[]` | +| `image.debug` | Enable image debug mode | `false` | + +### Redis® common configuration parameters + +| Name | Description | Value | +| -------------------------------- | ------------------------------------------------------------------------------------- | ------------- | +| `architecture` | Redis® architecture. Allowed values: `standalone` or `replication` | `replication` | +| `auth.enabled` | Enable password authentication | `true` | +| `auth.sentinel` | Enable password authentication on sentinels too | `true` | +| `auth.password` | Redis® password | `""` | +| `auth.existingSecret` | The name of an existing secret with Redis® credentials | `""` | +| `auth.existingSecretPasswordKey` | Password key to be retrieved from existing secret | `""` | +| `auth.usePasswordFiles` | Mount credentials as files instead of using an environment variable | `false` | +| `auth.usePasswordFileFromSecret` | Mount password file from secret | `true` | +| `commonConfiguration` | Common configuration to be added into the ConfigMap | `""` | +| `existingConfigmap` | The name of an existing ConfigMap with your custom configuration for Redis® nodes | `""` | + +### Redis® master configuration parameters + +| Name | Description | Value | +| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | +| `master.count` | Number of Redis® master instances to deploy (experimental, requires additional configuration) | `1` | +| `master.revisionHistoryLimit` | The number of old history to retain to allow rollback | `10` | +| `master.configuration` | Configuration for Redis® master nodes | `""` | +| `master.disableCommands` | Array with Redis® commands to disable on master nodes | `["FLUSHDB","FLUSHALL"]` | +| `master.command` | Override default container command (useful when using custom images) | `[]` | +| `master.args` | Override default container args (useful when using custom images) | `[]` | +| `master.enableServiceLinks` | Whether information about services should be injected into pod's environment variable | `true` | +| `master.preExecCmds` | Additional commands to run prior to starting Redis® master | `[]` | +| `master.extraFlags` | Array with additional command line flags for Redis® master | `[]` | +| `master.extraEnvVars` | Array with extra environment variables to add to Redis® master nodes | `[]` | +| `master.extraEnvVarsCM` | Name of existing ConfigMap containing extra env vars for Redis® master nodes | `""` | +| `master.extraEnvVarsSecret` | Name of existing Secret containing extra env vars for Redis® master nodes | `""` | +| `master.containerPorts.redis` | Container port to open on Redis® master nodes | `6379` | +| `master.startupProbe.enabled` | Enable startupProbe on Redis® master nodes | `false` | +| `master.startupProbe.initialDelaySeconds` | Initial delay seconds for startupProbe | `20` | +| `master.startupProbe.periodSeconds` | Period seconds for startupProbe | `5` | +| `master.startupProbe.timeoutSeconds` | Timeout seconds for startupProbe | `5` | +| `master.startupProbe.failureThreshold` | Failure threshold for startupProbe | `5` | +| `master.startupProbe.successThreshold` | Success threshold for startupProbe | `1` | +| `master.livenessProbe.enabled` | Enable livenessProbe on Redis® master nodes | `true` | +| `master.livenessProbe.initialDelaySeconds` | Initial delay seconds for livenessProbe | `20` | +| `master.livenessProbe.periodSeconds` | Period seconds for livenessProbe | `5` | +| `master.livenessProbe.timeoutSeconds` | Timeout seconds for livenessProbe | `5` | +| `master.livenessProbe.failureThreshold` | Failure threshold for livenessProbe | `5` | +| `master.livenessProbe.successThreshold` | Success threshold for livenessProbe | `1` | +| `master.readinessProbe.enabled` | Enable readinessProbe on Redis® master nodes | `true` | +| `master.readinessProbe.initialDelaySeconds` | Initial delay seconds for readinessProbe | `20` | +| `master.readinessProbe.periodSeconds` | Period seconds for readinessProbe | `5` | +| `master.readinessProbe.timeoutSeconds` | Timeout seconds for readinessProbe | `1` | +| `master.readinessProbe.failureThreshold` | Failure threshold for readinessProbe | `5` | +| `master.readinessProbe.successThreshold` | Success threshold for readinessProbe | `1` | +| `master.customStartupProbe` | Custom startupProbe that overrides the default one | `{}` | +| `master.customLivenessProbe` | Custom livenessProbe that overrides the default one | `{}` | +| `master.customReadinessProbe` | Custom readinessProbe that overrides the default one | `{}` | +| `master.resourcesPreset` | Set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). This is ignored if master.resources is set (master.resources is recommended for production). | `nano` | +| `master.resources` | Set container requests and limits for different resources like CPU or memory (essential for production workloads) | `{}` | +| `master.podSecurityContext.enabled` | Enabled Redis® master pods' Security Context | `true` | +| `master.podSecurityContext.fsGroupChangePolicy` | Set filesystem group change policy | `Always` | +| `master.podSecurityContext.sysctls` | Set kernel settings using the sysctl interface | `[]` | +| `master.podSecurityContext.supplementalGroups` | Set filesystem extra groups | `[]` | +| `master.podSecurityContext.fsGroup` | Set Redis® master pod's Security Context fsGroup | `1001` | +| `master.containerSecurityContext.enabled` | Enabled Redis® master containers' Security Context | `true` | +| `master.containerSecurityContext.seLinuxOptions` | Set SELinux options in container | `{}` | +| `master.containerSecurityContext.runAsUser` | Set Redis® master containers' Security Context runAsUser | `1001` | +| `master.containerSecurityContext.runAsGroup` | Set Redis® master containers' Security Context runAsGroup | `1001` | +| `master.containerSecurityContext.runAsNonRoot` | Set Redis® master containers' Security Context runAsNonRoot | `true` | +| `master.containerSecurityContext.allowPrivilegeEscalation` | Is it possible to escalate Redis® pod(s) privileges | `false` | +| `master.containerSecurityContext.readOnlyRootFilesystem` | Set container's Security Context read-only root filesystem | `true` | +| `master.containerSecurityContext.seccompProfile.type` | Set Redis® master containers' Security Context seccompProfile | `RuntimeDefault` | +| `master.containerSecurityContext.capabilities.drop` | Set Redis® master containers' Security Context capabilities to drop | `["ALL"]` | +| `master.kind` | Use either Deployment, StatefulSet (default) or DaemonSet | `StatefulSet` | +| `master.schedulerName` | Alternate scheduler for Redis® master pods | `""` | +| `master.updateStrategy.type` | Redis® master statefulset strategy type | `RollingUpdate` | +| `master.minReadySeconds` | How many seconds a pod needs to be ready before killing the next, during update | `0` | +| `master.priorityClassName` | Redis® master pods' priorityClassName | `""` | +| `master.automountServiceAccountToken` | Mount Service Account token in pod | `false` | +| `master.hostAliases` | Redis® master pods host aliases | `[]` | +| `master.podLabels` | Extra labels for Redis® master pods | `{}` | +| `master.podAnnotations` | Annotations for Redis® master pods | `{}` | +| `master.shareProcessNamespace` | Share a single process namespace between all of the containers in Redis® master pods | `false` | +| `master.podAffinityPreset` | Pod affinity preset. Ignored if `master.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `master.podAntiAffinityPreset` | Pod anti-affinity preset. Ignored if `master.affinity` is set. Allowed values: `soft` or `hard` | `soft` | +| `master.nodeAffinityPreset.type` | Node affinity preset type. Ignored if `master.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `master.nodeAffinityPreset.key` | Node label key to match. Ignored if `master.affinity` is set | `""` | +| `master.nodeAffinityPreset.values` | Node label values to match. Ignored if `master.affinity` is set | `[]` | +| `master.affinity` | Affinity for Redis® master pods assignment | `{}` | +| `master.nodeSelector` | Node labels for Redis® master pods assignment | `{}` | +| `master.tolerations` | Tolerations for Redis® master pods assignment | `[]` | +| `master.topologySpreadConstraints` | Spread Constraints for Redis® master pod assignment | `[]` | +| `master.dnsPolicy` | DNS Policy for Redis® master pod | `""` | +| `master.dnsConfig` | DNS Configuration for Redis® master pod | `{}` | +| `master.lifecycleHooks` | for the Redis® master container(s) to automate configuration before or after startup | `{}` | +| `master.extraVolumes` | Optionally specify extra list of additional volumes for the Redis® master pod(s) | `[]` | +| `master.extraVolumeMounts` | Optionally specify extra list of additional volumeMounts for the Redis® master container(s) | `[]` | +| `master.sidecars` | Add additional sidecar containers to the Redis® master pod(s) | `[]` | +| `master.initContainers` | Add additional init containers to the Redis® master pod(s) | `[]` | +| `master.persistence.enabled` | Enable persistence on Redis® master nodes using Persistent Volume Claims | `true` | +| `master.persistence.medium` | Provide a medium for `emptyDir` volumes. | `""` | +| `master.persistence.sizeLimit` | Set this to enable a size limit for `emptyDir` volumes. | `""` | +| `master.persistence.path` | The path the volume will be mounted at on Redis® master containers | `/data` | +| `master.persistence.subPath` | The subdirectory of the volume to mount on Redis® master containers | `""` | +| `master.persistence.subPathExpr` | Used to construct the subPath subdirectory of the volume to mount on Redis® master containers | `""` | +| `master.persistence.storageClass` | Persistent Volume storage class | `""` | +| `master.persistence.accessModes` | Persistent Volume access modes | `["ReadWriteOnce"]` | +| `master.persistence.size` | Persistent Volume size | `8Gi` | +| `master.persistence.annotations` | Additional custom annotations for the PVC | `{}` | +| `master.persistence.labels` | Additional custom labels for the PVC | `{}` | +| `master.persistence.selector` | Additional labels to match for the PVC | `{}` | +| `master.persistence.dataSource` | Custom PVC data source | `{}` | +| `master.persistence.existingClaim` | Use a existing PVC which must be created manually before bound | `""` | +| `master.persistentVolumeClaimRetentionPolicy.enabled` | Controls if and how PVCs are deleted during the lifecycle of a StatefulSet | `false` | +| `master.persistentVolumeClaimRetentionPolicy.whenScaled` | Volume retention behavior when the replica count of the StatefulSet is reduced | `Retain` | +| `master.persistentVolumeClaimRetentionPolicy.whenDeleted` | Volume retention behavior that applies when the StatefulSet is deleted | `Retain` | +| `master.service.type` | Redis® master service type | `ClusterIP` | +| `master.service.portNames.redis` | Redis® master service port name | `tcp-redis` | +| `master.service.ports.redis` | Redis® master service port | `6379` | +| `master.service.nodePorts.redis` | Node port for Redis® master | `""` | +| `master.service.externalTrafficPolicy` | Redis® master service external traffic policy | `Cluster` | +| `master.service.extraPorts` | Extra ports to expose (normally used with the `sidecar` value) | `[]` | +| `master.service.internalTrafficPolicy` | Redis® master service internal traffic policy (requires Kubernetes v1.22 or greater to be usable) | `Cluster` | +| `master.service.clusterIP` | Redis® master service Cluster IP | `""` | +| `master.service.loadBalancerIP` | Redis® master service Load Balancer IP | `""` | +| `master.service.loadBalancerClass` | master service Load Balancer class if service type is `LoadBalancer` (optional, cloud specific) | `""` | +| `master.service.loadBalancerSourceRanges` | Redis® master service Load Balancer sources | `[]` | +| `master.service.externalIPs` | Redis® master service External IPs | `[]` | +| `master.service.annotations` | Additional custom annotations for Redis® master service | `{}` | +| `master.service.sessionAffinity` | Session Affinity for Kubernetes service, can be "None" or "ClientIP" | `None` | +| `master.service.sessionAffinityConfig` | Additional settings for the sessionAffinity | `{}` | +| `master.terminationGracePeriodSeconds` | Integer setting the termination grace period for the redis-master pods | `30` | +| `master.serviceAccount.create` | Specifies whether a ServiceAccount should be created | `true` | +| `master.serviceAccount.name` | The name of the ServiceAccount to use. | `""` | +| `master.serviceAccount.automountServiceAccountToken` | Whether to auto mount the service account token | `false` | +| `master.serviceAccount.annotations` | Additional custom annotations for the ServiceAccount | `{}` | +| `master.pdb.create` | Enable/disable a Pod Disruption Budget creation | `true` | +| `master.pdb.minAvailable` | Minimum number/percentage of pods that should remain scheduled | `{}` | +| `master.pdb.maxUnavailable` | Maximum number/percentage of pods that may be made unavailable. Defaults to `1` if both `master.pdb.minAvailable` and `master.pdb.maxUnavailable` are empty. | `{}` | +| `master.extraPodSpec` | Optionally specify extra PodSpec for the Redis® master pod(s) | `{}` | +| `master.annotations` | Additional custom annotations for Redis® Master resource | `{}` | + +### Redis® replicas configuration parameters + +| Name | Description | Value | +| ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | +| `replica.kind` | Use either DaemonSet or StatefulSet (default) | `StatefulSet` | +| `replica.replicaCount` | Number of Redis® replicas to deploy | `3` | +| `replica.revisionHistoryLimit` | The number of old history to retain to allow rollback | `10` | +| `replica.configuration` | Configuration for Redis® replicas nodes | `""` | +| `replica.disableCommands` | Array with Redis® commands to disable on replicas nodes | `["FLUSHDB","FLUSHALL"]` | +| `replica.command` | Override default container command (useful when using custom images) | `[]` | +| `replica.args` | Override default container args (useful when using custom images) | `[]` | +| `replica.enableServiceLinks` | Whether information about services should be injected into pod's environment variable | `true` | +| `replica.preExecCmds` | Additional commands to run prior to starting Redis® replicas | `[]` | +| `replica.extraFlags` | Array with additional command line flags for Redis® replicas | `[]` | +| `replica.extraEnvVars` | Array with extra environment variables to add to Redis® replicas nodes | `[]` | +| `replica.extraEnvVarsCM` | Name of existing ConfigMap containing extra env vars for Redis® replicas nodes | `""` | +| `replica.extraEnvVarsSecret` | Name of existing Secret containing extra env vars for Redis® replicas nodes | `""` | +| `replica.externalMaster.enabled` | Use external master for bootstrapping | `false` | +| `replica.externalMaster.host` | External master host to bootstrap from | `""` | +| `replica.externalMaster.port` | Port for Redis service external master host | `6379` | +| `replica.containerPorts.redis` | Container port to open on Redis® replicas nodes | `6379` | +| `replica.startupProbe.enabled` | Enable startupProbe on Redis® replicas nodes | `true` | +| `replica.startupProbe.initialDelaySeconds` | Initial delay seconds for startupProbe | `10` | +| `replica.startupProbe.periodSeconds` | Period seconds for startupProbe | `10` | +| `replica.startupProbe.timeoutSeconds` | Timeout seconds for startupProbe | `5` | +| `replica.startupProbe.failureThreshold` | Failure threshold for startupProbe | `22` | +| `replica.startupProbe.successThreshold` | Success threshold for startupProbe | `1` | +| `replica.livenessProbe.enabled` | Enable livenessProbe on Redis® replicas nodes | `true` | +| `replica.livenessProbe.initialDelaySeconds` | Initial delay seconds for livenessProbe | `20` | +| `replica.livenessProbe.periodSeconds` | Period seconds for livenessProbe | `5` | +| `replica.livenessProbe.timeoutSeconds` | Timeout seconds for livenessProbe | `5` | +| `replica.livenessProbe.failureThreshold` | Failure threshold for livenessProbe | `5` | +| `replica.livenessProbe.successThreshold` | Success threshold for livenessProbe | `1` | +| `replica.readinessProbe.enabled` | Enable readinessProbe on Redis® replicas nodes | `true` | +| `replica.readinessProbe.initialDelaySeconds` | Initial delay seconds for readinessProbe | `20` | +| `replica.readinessProbe.periodSeconds` | Period seconds for readinessProbe | `5` | +| `replica.readinessProbe.timeoutSeconds` | Timeout seconds for readinessProbe | `1` | +| `replica.readinessProbe.failureThreshold` | Failure threshold for readinessProbe | `5` | +| `replica.readinessProbe.successThreshold` | Success threshold for readinessProbe | `1` | +| `replica.customStartupProbe` | Custom startupProbe that overrides the default one | `{}` | +| `replica.customLivenessProbe` | Custom livenessProbe that overrides the default one | `{}` | +| `replica.customReadinessProbe` | Custom readinessProbe that overrides the default one | `{}` | +| `replica.resourcesPreset` | Set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). This is ignored if replica.resources is set (replica.resources is recommended for production). | `nano` | +| `replica.resources` | Set container requests and limits for different resources like CPU or memory (essential for production workloads) | `{}` | +| `replica.podSecurityContext.enabled` | Enabled Redis® replicas pods' Security Context | `true` | +| `replica.podSecurityContext.fsGroupChangePolicy` | Set filesystem group change policy | `Always` | +| `replica.podSecurityContext.sysctls` | Set kernel settings using the sysctl interface | `[]` | +| `replica.podSecurityContext.supplementalGroups` | Set filesystem extra groups | `[]` | +| `replica.podSecurityContext.fsGroup` | Set Redis® replicas pod's Security Context fsGroup | `1001` | +| `replica.containerSecurityContext.enabled` | Enabled Redis® replicas containers' Security Context | `true` | +| `replica.containerSecurityContext.seLinuxOptions` | Set SELinux options in container | `{}` | +| `replica.containerSecurityContext.runAsUser` | Set Redis® replicas containers' Security Context runAsUser | `1001` | +| `replica.containerSecurityContext.runAsGroup` | Set Redis® replicas containers' Security Context runAsGroup | `1001` | +| `replica.containerSecurityContext.runAsNonRoot` | Set Redis® replicas containers' Security Context runAsNonRoot | `true` | +| `replica.containerSecurityContext.allowPrivilegeEscalation` | Set Redis® replicas pod's Security Context allowPrivilegeEscalation | `false` | +| `replica.containerSecurityContext.readOnlyRootFilesystem` | Set container's Security Context read-only root filesystem | `true` | +| `replica.containerSecurityContext.seccompProfile.type` | Set Redis® replicas containers' Security Context seccompProfile | `RuntimeDefault` | +| `replica.containerSecurityContext.capabilities.drop` | Set Redis® replicas containers' Security Context capabilities to drop | `["ALL"]` | +| `replica.schedulerName` | Alternate scheduler for Redis® replicas pods | `""` | +| `replica.updateStrategy.type` | Redis® replicas statefulset strategy type | `RollingUpdate` | +| `replica.minReadySeconds` | How many seconds a pod needs to be ready before killing the next, during update | `0` | +| `replica.priorityClassName` | Redis® replicas pods' priorityClassName | `""` | +| `replica.podManagementPolicy` | podManagementPolicy to manage scaling operation of %%MAIN_CONTAINER_NAME%% pods | `""` | +| `replica.automountServiceAccountToken` | Mount Service Account token in pod | `false` | +| `replica.hostAliases` | Redis® replicas pods host aliases | `[]` | +| `replica.podLabels` | Extra labels for Redis® replicas pods | `{}` | +| `replica.podAnnotations` | Annotations for Redis® replicas pods | `{}` | +| `replica.shareProcessNamespace` | Share a single process namespace between all of the containers in Redis® replicas pods | `false` | +| `replica.podAffinityPreset` | Pod affinity preset. Ignored if `replica.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `replica.podAntiAffinityPreset` | Pod anti-affinity preset. Ignored if `replica.affinity` is set. Allowed values: `soft` or `hard` | `soft` | +| `replica.nodeAffinityPreset.type` | Node affinity preset type. Ignored if `replica.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `replica.nodeAffinityPreset.key` | Node label key to match. Ignored if `replica.affinity` is set | `""` | +| `replica.nodeAffinityPreset.values` | Node label values to match. Ignored if `replica.affinity` is set | `[]` | +| `replica.affinity` | Affinity for Redis® replicas pods assignment | `{}` | +| `replica.nodeSelector` | Node labels for Redis® replicas pods assignment | `{}` | +| `replica.tolerations` | Tolerations for Redis® replicas pods assignment | `[]` | +| `replica.topologySpreadConstraints` | Spread Constraints for Redis® replicas pod assignment | `[]` | +| `replica.dnsPolicy` | DNS Policy for Redis® replica pods | `""` | +| `replica.dnsConfig` | DNS Configuration for Redis® replica pods | `{}` | +| `replica.lifecycleHooks` | for the Redis® replica container(s) to automate configuration before or after startup | `{}` | +| `replica.extraVolumes` | Optionally specify extra list of additional volumes for the Redis® replicas pod(s) | `[]` | +| `replica.extraVolumeMounts` | Optionally specify extra list of additional volumeMounts for the Redis® replicas container(s) | `[]` | +| `replica.sidecars` | Add additional sidecar containers to the Redis® replicas pod(s) | `[]` | +| `replica.initContainers` | Add additional init containers to the Redis® replicas pod(s) | `[]` | +| `replica.persistence.enabled` | Enable persistence on Redis® replicas nodes using Persistent Volume Claims | `true` | +| `replica.persistence.medium` | Provide a medium for `emptyDir` volumes. | `""` | +| `replica.persistence.sizeLimit` | Set this to enable a size limit for `emptyDir` volumes. | `""` | +| `replica.persistence.path` | The path the volume will be mounted at on Redis® replicas containers | `/data` | +| `replica.persistence.subPath` | The subdirectory of the volume to mount on Redis® replicas containers | `""` | +| `replica.persistence.subPathExpr` | Used to construct the subPath subdirectory of the volume to mount on Redis® replicas containers | `""` | +| `replica.persistence.storageClass` | Persistent Volume storage class | `""` | +| `replica.persistence.accessModes` | Persistent Volume access modes | `["ReadWriteOnce"]` | +| `replica.persistence.size` | Persistent Volume size | `8Gi` | +| `replica.persistence.annotations` | Additional custom annotations for the PVC | `{}` | +| `replica.persistence.labels` | Additional custom labels for the PVC | `{}` | +| `replica.persistence.selector` | Additional labels to match for the PVC | `{}` | +| `replica.persistence.dataSource` | Custom PVC data source | `{}` | +| `replica.persistence.existingClaim` | Use a existing PVC which must be created manually before bound | `""` | +| `replica.persistentVolumeClaimRetentionPolicy.enabled` | Controls if and how PVCs are deleted during the lifecycle of a StatefulSet | `false` | +| `replica.persistentVolumeClaimRetentionPolicy.whenScaled` | Volume retention behavior when the replica count of the StatefulSet is reduced | `Retain` | +| `replica.persistentVolumeClaimRetentionPolicy.whenDeleted` | Volume retention behavior that applies when the StatefulSet is deleted | `Retain` | +| `replica.service.type` | Redis® replicas service type | `ClusterIP` | +| `replica.service.ports.redis` | Redis® replicas service port | `6379` | +| `replica.service.nodePorts.redis` | Node port for Redis® replicas | `""` | +| `replica.service.externalTrafficPolicy` | Redis® replicas service external traffic policy | `Cluster` | +| `replica.service.internalTrafficPolicy` | Redis® replicas service internal traffic policy (requires Kubernetes v1.22 or greater to be usable) | `Cluster` | +| `replica.service.extraPorts` | Extra ports to expose (normally used with the `sidecar` value) | `[]` | +| `replica.service.clusterIP` | Redis® replicas service Cluster IP | `""` | +| `replica.service.loadBalancerIP` | Redis® replicas service Load Balancer IP | `""` | +| `replica.service.loadBalancerClass` | replicas service Load Balancer class if service type is `LoadBalancer` (optional, cloud specific) | `""` | +| `replica.service.loadBalancerSourceRanges` | Redis® replicas service Load Balancer sources | `[]` | +| `replica.service.annotations` | Additional custom annotations for Redis® replicas service | `{}` | +| `replica.service.sessionAffinity` | Session Affinity for Kubernetes service, can be "None" or "ClientIP" | `None` | +| `replica.service.sessionAffinityConfig` | Additional settings for the sessionAffinity | `{}` | +| `replica.terminationGracePeriodSeconds` | Integer setting the termination grace period for the redis-replicas pods | `30` | +| `replica.autoscaling.enabled` | Enable replica autoscaling settings | `false` | +| `replica.autoscaling.minReplicas` | Minimum replicas for the pod autoscaling | `1` | +| `replica.autoscaling.maxReplicas` | Maximum replicas for the pod autoscaling | `11` | +| `replica.autoscaling.targetCPU` | Percentage of CPU to consider when autoscaling | `""` | +| `replica.autoscaling.targetMemory` | Percentage of Memory to consider when autoscaling | `""` | +| `replica.serviceAccount.create` | Specifies whether a ServiceAccount should be created | `true` | +| `replica.serviceAccount.name` | The name of the ServiceAccount to use. | `""` | +| `replica.serviceAccount.automountServiceAccountToken` | Whether to auto mount the service account token | `false` | +| `replica.serviceAccount.annotations` | Additional custom annotations for the ServiceAccount | `{}` | +| `replica.pdb.create` | Enable/disable a Pod Disruption Budget creation | `true` | +| `replica.pdb.minAvailable` | Minimum number/percentage of pods that should remain scheduled | `{}` | +| `replica.pdb.maxUnavailable` | Maximum number/percentage of pods that may be made unavailable. Defaults to `1` if both `replica.pdb.minAvailable` and `replica.pdb.maxUnavailable` are empty. | `{}` | +| `replica.extraPodSpec` | Optionally specify extra PodSpec for the Redis® replicas pod(s) | `{}` | +| `replica.annotations` | Additional custom annotations for Redis® replicas resource | `{}` | + +### Redis® Sentinel configuration parameters + +| Name | Description | Value | +| ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| `sentinel.enabled` | Use Redis® Sentinel on Redis® pods. | `false` | +| `sentinel.image.registry` | Redis® Sentinel image registry | `REGISTRY_NAME` | +| `sentinel.image.repository` | Redis® Sentinel image repository | `REPOSITORY_NAME/redis-sentinel` | +| `sentinel.image.digest` | Redis® Sentinel image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `sentinel.image.pullPolicy` | Redis® Sentinel image pull policy | `IfNotPresent` | +| `sentinel.image.pullSecrets` | Redis® Sentinel image pull secrets | `[]` | +| `sentinel.image.debug` | Enable image debug mode | `false` | +| `sentinel.annotations` | Additional custom annotations for Redis® Sentinel resource | `{}` | +| `sentinel.masterSet` | Master set name | `mymaster` | +| `sentinel.quorum` | Sentinel Quorum | `2` | +| `sentinel.getMasterTimeout` | Amount of time to allow before get_sentinel_master_info() times out. | `90` | +| `sentinel.automateClusterRecovery` | Automate cluster recovery in cases where the last replica is not considered a good replica and Sentinel won't automatically failover to it. | `false` | +| `sentinel.redisShutdownWaitFailover` | Whether the Redis® master container waits for the failover at shutdown (in addition to the Redis® Sentinel container). | `true` | +| `sentinel.downAfterMilliseconds` | Timeout for detecting a Redis® node is down | `60000` | +| `sentinel.failoverTimeout` | Timeout for performing a election failover | `180000` | +| `sentinel.parallelSyncs` | Number of replicas that can be reconfigured in parallel to use the new master after a failover | `1` | +| `sentinel.configuration` | Configuration for Redis® Sentinel nodes | `""` | +| `sentinel.command` | Override default container command (useful when using custom images) | `[]` | +| `sentinel.args` | Override default container args (useful when using custom images) | `[]` | +| `sentinel.enableServiceLinks` | Whether information about services should be injected into pod's environment variable | `true` | +| `sentinel.preExecCmds` | Additional commands to run prior to starting Redis® Sentinel | `[]` | +| `sentinel.extraEnvVars` | Array with extra environment variables to add to Redis® Sentinel nodes | `[]` | +| `sentinel.extraEnvVarsCM` | Name of existing ConfigMap containing extra env vars for Redis® Sentinel nodes | `""` | +| `sentinel.extraEnvVarsSecret` | Name of existing Secret containing extra env vars for Redis® Sentinel nodes | `""` | +| `sentinel.externalMaster.enabled` | Use external master for bootstrapping | `false` | +| `sentinel.externalMaster.host` | External master host to bootstrap from | `""` | +| `sentinel.externalMaster.port` | Port for Redis service external master host | `6379` | +| `sentinel.containerPorts.sentinel` | Container port to open on Redis® Sentinel nodes | `26379` | +| `sentinel.startupProbe.enabled` | Enable startupProbe on Redis® Sentinel nodes | `true` | +| `sentinel.startupProbe.initialDelaySeconds` | Initial delay seconds for startupProbe | `10` | +| `sentinel.startupProbe.periodSeconds` | Period seconds for startupProbe | `10` | +| `sentinel.startupProbe.timeoutSeconds` | Timeout seconds for startupProbe | `5` | +| `sentinel.startupProbe.failureThreshold` | Failure threshold for startupProbe | `22` | +| `sentinel.startupProbe.successThreshold` | Success threshold for startupProbe | `1` | +| `sentinel.livenessProbe.enabled` | Enable livenessProbe on Redis® Sentinel nodes | `true` | +| `sentinel.livenessProbe.initialDelaySeconds` | Initial delay seconds for livenessProbe | `20` | +| `sentinel.livenessProbe.periodSeconds` | Period seconds for livenessProbe | `10` | +| `sentinel.livenessProbe.timeoutSeconds` | Timeout seconds for livenessProbe | `5` | +| `sentinel.livenessProbe.failureThreshold` | Failure threshold for livenessProbe | `6` | +| `sentinel.livenessProbe.successThreshold` | Success threshold for livenessProbe | `1` | +| `sentinel.readinessProbe.enabled` | Enable readinessProbe on Redis® Sentinel nodes | `true` | +| `sentinel.readinessProbe.initialDelaySeconds` | Initial delay seconds for readinessProbe | `20` | +| `sentinel.readinessProbe.periodSeconds` | Period seconds for readinessProbe | `5` | +| `sentinel.readinessProbe.timeoutSeconds` | Timeout seconds for readinessProbe | `1` | +| `sentinel.readinessProbe.failureThreshold` | Failure threshold for readinessProbe | `6` | +| `sentinel.readinessProbe.successThreshold` | Success threshold for readinessProbe | `1` | +| `sentinel.customStartupProbe` | Custom startupProbe that overrides the default one | `{}` | +| `sentinel.customLivenessProbe` | Custom livenessProbe that overrides the default one | `{}` | +| `sentinel.customReadinessProbe` | Custom readinessProbe that overrides the default one | `{}` | +| `sentinel.persistence.enabled` | Enable persistence on Redis® sentinel nodes using Persistent Volume Claims (Experimental) | `false` | +| `sentinel.persistence.storageClass` | Persistent Volume storage class | `""` | +| `sentinel.persistence.accessModes` | Persistent Volume access modes | `["ReadWriteOnce"]` | +| `sentinel.persistence.size` | Persistent Volume size | `100Mi` | +| `sentinel.persistence.annotations` | Additional custom annotations for the PVC | `{}` | +| `sentinel.persistence.labels` | Additional custom labels for the PVC | `{}` | +| `sentinel.persistence.selector` | Additional labels to match for the PVC | `{}` | +| `sentinel.persistence.dataSource` | Custom PVC data source | `{}` | +| `sentinel.persistence.medium` | Provide a medium for `emptyDir` volumes. | `""` | +| `sentinel.persistence.sizeLimit` | Set this to enable a size limit for `emptyDir` volumes. | `""` | +| `sentinel.persistentVolumeClaimRetentionPolicy.enabled` | Controls if and how PVCs are deleted during the lifecycle of a StatefulSet | `false` | +| `sentinel.persistentVolumeClaimRetentionPolicy.whenScaled` | Volume retention behavior when the replica count of the StatefulSet is reduced | `Retain` | +| `sentinel.persistentVolumeClaimRetentionPolicy.whenDeleted` | Volume retention behavior that applies when the StatefulSet is deleted | `Retain` | +| `sentinel.resourcesPreset` | Set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). This is ignored if sentinel.resources is set (sentinel.resources is recommended for production). | `nano` | +| `sentinel.resources` | Set container requests and limits for different resources like CPU or memory (essential for production workloads) | `{}` | +| `sentinel.containerSecurityContext.enabled` | Enabled Redis® Sentinel containers' Security Context | `true` | +| `sentinel.containerSecurityContext.seLinuxOptions` | Set SELinux options in container | `{}` | +| `sentinel.containerSecurityContext.runAsUser` | Set Redis® Sentinel containers' Security Context runAsUser | `1001` | +| `sentinel.containerSecurityContext.runAsGroup` | Set Redis® Sentinel containers' Security Context runAsGroup | `1001` | +| `sentinel.containerSecurityContext.runAsNonRoot` | Set Redis® Sentinel containers' Security Context runAsNonRoot | `true` | +| `sentinel.containerSecurityContext.readOnlyRootFilesystem` | Set container's Security Context read-only root filesystem | `true` | +| `sentinel.containerSecurityContext.allowPrivilegeEscalation` | Set Redis® Sentinel containers' Security Context allowPrivilegeEscalation | `false` | +| `sentinel.containerSecurityContext.seccompProfile.type` | Set Redis® Sentinel containers' Security Context seccompProfile | `RuntimeDefault` | +| `sentinel.containerSecurityContext.capabilities.drop` | Set Redis® Sentinel containers' Security Context capabilities to drop | `["ALL"]` | +| `sentinel.lifecycleHooks` | for the Redis® sentinel container(s) to automate configuration before or after startup | `{}` | +| `sentinel.extraVolumes` | Optionally specify extra list of additional volumes for the Redis® Sentinel | `[]` | +| `sentinel.extraVolumeMounts` | Optionally specify extra list of additional volumeMounts for the Redis® Sentinel container(s) | `[]` | +| `sentinel.service.type` | Redis® Sentinel service type | `ClusterIP` | +| `sentinel.service.ports.redis` | Redis® service port for Redis® | `6379` | +| `sentinel.service.ports.sentinel` | Redis® service port for Redis® Sentinel | `26379` | +| `sentinel.service.nodePorts.redis` | Node port for Redis® | `""` | +| `sentinel.service.nodePorts.sentinel` | Node port for Sentinel | `""` | +| `sentinel.service.externalTrafficPolicy` | Redis® Sentinel service external traffic policy | `Cluster` | +| `sentinel.service.extraPorts` | Extra ports to expose (normally used with the `sidecar` value) | `[]` | +| `sentinel.service.clusterIP` | Redis® Sentinel service Cluster IP | `""` | +| `sentinel.service.createMaster` | Enable master service pointing to the current master (experimental) | `false` | +| `sentinel.service.loadBalancerIP` | Redis® Sentinel service Load Balancer IP | `""` | +| `sentinel.service.loadBalancerClass` | sentinel service Load Balancer class if service type is `LoadBalancer` (optional, cloud specific) | `""` | +| `sentinel.service.loadBalancerSourceRanges` | Redis® Sentinel service Load Balancer sources | `[]` | +| `sentinel.service.annotations` | Additional custom annotations for Redis® Sentinel service | `{}` | +| `sentinel.service.sessionAffinity` | Session Affinity for Kubernetes service, can be "None" or "ClientIP" | `None` | +| `sentinel.service.sessionAffinityConfig` | Additional settings for the sessionAffinity | `{}` | +| `sentinel.service.headless.annotations` | Annotations for the headless service. | `{}` | +| `sentinel.service.headless.extraPorts` | Optionally specify extra ports to expose for the headless service. | `[]` | +| `sentinel.masterService.enabled` | Enable master service pointing to the current master (experimental) | `false` | +| `sentinel.masterService.type` | Redis® Sentinel master service type | `ClusterIP` | +| `sentinel.masterService.ports.redis` | Redis® service port for Redis® | `6379` | +| `sentinel.masterService.nodePorts.redis` | Node port for Redis® | `""` | +| `sentinel.masterService.externalTrafficPolicy` | Redis® master service external traffic policy | `""` | +| `sentinel.masterService.extraPorts` | Extra ports to expose (normally used with the `sidecar` value) | `[]` | +| `sentinel.masterService.clusterIP` | Redis® master service Cluster IP | `""` | +| `sentinel.masterService.loadBalancerIP` | Redis® master service Load Balancer IP | `""` | +| `sentinel.masterService.loadBalancerClass` | master service Load Balancer class if service type is `LoadBalancer` (optional, cloud specific) | `""` | +| `sentinel.masterService.loadBalancerSourceRanges` | Redis® master service Load Balancer sources | `[]` | +| `sentinel.masterService.annotations` | Additional custom annotations for Redis® master service | `{}` | +| `sentinel.masterService.sessionAffinity` | Session Affinity for Kubernetes service, can be "None" or "ClientIP" | `None` | +| `sentinel.masterService.sessionAffinityConfig` | Additional settings for the sessionAffinity | `{}` | +| `sentinel.terminationGracePeriodSeconds` | Integer setting the termination grace period for the redis-node pods | `30` | +| `sentinel.extraPodSpec` | Optionally specify extra PodSpec for the Redis® Sentinel pod(s) | `{}` | + +### Other Parameters + +| Name | Description | Value | +| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| `serviceBindings.enabled` | Create secret for service binding (Experimental) | `false` | +| `networkPolicy.enabled` | Enable creation of NetworkPolicy resources | `true` | +| `networkPolicy.allowExternal` | Don't require client label for connections | `true` | +| `networkPolicy.allowExternalEgress` | Allow the pod to access any range of port and all destinations. | `true` | +| `networkPolicy.extraIngress` | Add extra ingress rules to the NetworkPolicy | `[]` | +| `networkPolicy.extraEgress` | Add extra egress rules to the NetworkPolicy | `[]` | +| `networkPolicy.ingressNSMatchLabels` | Labels to match to allow traffic from other namespaces | `{}` | +| `networkPolicy.ingressNSPodMatchLabels` | Pod labels to match to allow traffic from other namespaces | `{}` | +| `networkPolicy.metrics.allowExternal` | Don't require client label for connections for metrics endpoint | `true` | +| `networkPolicy.metrics.ingressNSMatchLabels` | Labels to match to allow traffic from other namespaces to metrics endpoint | `{}` | +| `networkPolicy.metrics.ingressNSPodMatchLabels` | Pod labels to match to allow traffic from other namespaces to metrics endpoint | `{}` | +| `podSecurityPolicy.create` | Whether to create a PodSecurityPolicy. WARNING: PodSecurityPolicy is deprecated in Kubernetes v1.21 or later, unavailable in v1.25 or later | `false` | +| `podSecurityPolicy.enabled` | Enable PodSecurityPolicy's RBAC rules | `false` | +| `rbac.create` | Specifies whether RBAC resources should be created | `false` | +| `rbac.rules` | Custom RBAC rules to set | `[]` | +| `serviceAccount.create` | Specifies whether a ServiceAccount should be created | `true` | +| `serviceAccount.name` | The name of the ServiceAccount to use. | `""` | +| `serviceAccount.automountServiceAccountToken` | Whether to auto mount the service account token | `false` | +| `serviceAccount.annotations` | Additional custom annotations for the ServiceAccount | `{}` | +| `pdb` | DEPRECATED Please use `master.pdb` and `replica.pdb` values instead | `{}` | +| `tls.enabled` | Enable TLS traffic | `false` | +| `tls.authClients` | Require clients to authenticate | `true` | +| `tls.autoGenerated` | Enable autogenerated certificates | `false` | +| `tls.existingSecret` | The name of the existing secret that contains the TLS certificates | `""` | +| `tls.certificatesSecret` | DEPRECATED. Use existingSecret instead. | `""` | +| `tls.certFilename` | Certificate filename | `""` | +| `tls.certKeyFilename` | Certificate Key filename | `""` | +| `tls.certCAFilename` | CA Certificate filename | `""` | +| `tls.dhParamsFilename` | File containing DH params (in order to support DH based ciphers) | `""` | + +### Metrics Parameters + +| Name | Description | Value | +| ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | +| `metrics.enabled` | Start a sidecar prometheus exporter to expose Redis® metrics | `false` | +| `metrics.image.registry` | Redis® Exporter image registry | `REGISTRY_NAME` | +| `metrics.image.repository` | Redis® Exporter image repository | `REPOSITORY_NAME/redis-exporter` | +| `metrics.image.digest` | Redis® Exporter image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `metrics.image.pullPolicy` | Redis® Exporter image pull policy | `IfNotPresent` | +| `metrics.image.pullSecrets` | Redis® Exporter image pull secrets | `[]` | +| `metrics.containerPorts.http` | Metrics HTTP container port | `9121` | +| `metrics.startupProbe.enabled` | Enable startupProbe on Redis® replicas nodes | `false` | +| `metrics.startupProbe.initialDelaySeconds` | Initial delay seconds for startupProbe | `10` | +| `metrics.startupProbe.periodSeconds` | Period seconds for startupProbe | `10` | +| `metrics.startupProbe.timeoutSeconds` | Timeout seconds for startupProbe | `5` | +| `metrics.startupProbe.failureThreshold` | Failure threshold for startupProbe | `5` | +| `metrics.startupProbe.successThreshold` | Success threshold for startupProbe | `1` | +| `metrics.livenessProbe.enabled` | Enable livenessProbe on Redis® replicas nodes | `true` | +| `metrics.livenessProbe.initialDelaySeconds` | Initial delay seconds for livenessProbe | `10` | +| `metrics.livenessProbe.periodSeconds` | Period seconds for livenessProbe | `10` | +| `metrics.livenessProbe.timeoutSeconds` | Timeout seconds for livenessProbe | `5` | +| `metrics.livenessProbe.failureThreshold` | Failure threshold for livenessProbe | `5` | +| `metrics.livenessProbe.successThreshold` | Success threshold for livenessProbe | `1` | +| `metrics.readinessProbe.enabled` | Enable readinessProbe on Redis® replicas nodes | `true` | +| `metrics.readinessProbe.initialDelaySeconds` | Initial delay seconds for readinessProbe | `5` | +| `metrics.readinessProbe.periodSeconds` | Period seconds for readinessProbe | `10` | +| `metrics.readinessProbe.timeoutSeconds` | Timeout seconds for readinessProbe | `1` | +| `metrics.readinessProbe.failureThreshold` | Failure threshold for readinessProbe | `3` | +| `metrics.readinessProbe.successThreshold` | Success threshold for readinessProbe | `1` | +| `metrics.customStartupProbe` | Custom startupProbe that overrides the default one | `{}` | +| `metrics.customLivenessProbe` | Custom livenessProbe that overrides the default one | `{}` | +| `metrics.customReadinessProbe` | Custom readinessProbe that overrides the default one | `{}` | +| `metrics.command` | Override default metrics container init command (useful when using custom images) | `[]` | +| `metrics.redisTargetHost` | A way to specify an alternative Redis® hostname | `localhost` | +| `metrics.extraArgs` | Extra arguments for Redis® exporter, for example: | `{}` | +| `metrics.extraEnvVars` | Array with extra environment variables to add to Redis® exporter | `[]` | +| `metrics.containerSecurityContext.enabled` | Enabled Redis® exporter containers' Security Context | `true` | +| `metrics.containerSecurityContext.seLinuxOptions` | Set SELinux options in container | `{}` | +| `metrics.containerSecurityContext.runAsUser` | Set Redis® exporter containers' Security Context runAsUser | `1001` | +| `metrics.containerSecurityContext.runAsGroup` | Set Redis® exporter containers' Security Context runAsGroup | `1001` | +| `metrics.containerSecurityContext.runAsNonRoot` | Set Redis® exporter containers' Security Context runAsNonRoot | `true` | +| `metrics.containerSecurityContext.allowPrivilegeEscalation` | Set Redis® exporter containers' Security Context allowPrivilegeEscalation | `false` | +| `metrics.containerSecurityContext.readOnlyRootFilesystem` | Set container's Security Context read-only root filesystem | `true` | +| `metrics.containerSecurityContext.seccompProfile.type` | Set Redis® exporter containers' Security Context seccompProfile | `RuntimeDefault` | +| `metrics.containerSecurityContext.capabilities.drop` | Set Redis® exporter containers' Security Context capabilities to drop | `["ALL"]` | +| `metrics.extraVolumes` | Optionally specify extra list of additional volumes for the Redis® metrics sidecar | `[]` | +| `metrics.extraVolumeMounts` | Optionally specify extra list of additional volumeMounts for the Redis® metrics sidecar | `[]` | +| `metrics.resourcesPreset` | Set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). This is ignored if metrics.resources is set (metrics.resources is recommended for production). | `nano` | +| `metrics.resources` | Set container requests and limits for different resources like CPU or memory (essential for production workloads) | `{}` | +| `metrics.podLabels` | Extra labels for Redis® exporter pods | `{}` | +| `metrics.podAnnotations` | Annotations for Redis® exporter pods | `{}` | +| `metrics.service.enabled` | Create Service resource(s) for scraping metrics using PrometheusOperator ServiceMonitor, can be disabled when using a PodMonitor | `true` | +| `metrics.service.type` | Redis® exporter service type | `ClusterIP` | +| `metrics.service.ports.http` | Redis® exporter service port | `9121` | +| `metrics.service.externalTrafficPolicy` | Redis® exporter service external traffic policy | `Cluster` | +| `metrics.service.extraPorts` | Extra ports to expose (normally used with the `sidecar` value) | `[]` | +| `metrics.service.loadBalancerIP` | Redis® exporter service Load Balancer IP | `""` | +| `metrics.service.loadBalancerClass` | exporter service Load Balancer class if service type is `LoadBalancer` (optional, cloud specific) | `""` | +| `metrics.service.loadBalancerSourceRanges` | Redis® exporter service Load Balancer sources | `[]` | +| `metrics.service.annotations` | Additional custom annotations for Redis® exporter service | `{}` | +| `metrics.service.clusterIP` | Redis® exporter service Cluster IP | `""` | +| `metrics.serviceMonitor.port` | the service port to scrape metrics from | `http-metrics` | +| `metrics.serviceMonitor.enabled` | Create ServiceMonitor resource(s) for scraping metrics using PrometheusOperator | `false` | +| `metrics.serviceMonitor.namespace` | The namespace in which the ServiceMonitor will be created | `""` | +| `metrics.serviceMonitor.interval` | The interval at which metrics should be scraped | `30s` | +| `metrics.serviceMonitor.scrapeTimeout` | The timeout after which the scrape is ended | `""` | +| `metrics.serviceMonitor.relabelings` | Metrics RelabelConfigs to apply to samples before scraping. | `[]` | +| `metrics.serviceMonitor.metricRelabelings` | Metrics RelabelConfigs to apply to samples before ingestion. | `[]` | +| `metrics.serviceMonitor.honorLabels` | Specify honorLabels parameter to add the scrape endpoint | `false` | +| `metrics.serviceMonitor.additionalLabels` | Additional labels that can be used so ServiceMonitor resource(s) can be discovered by Prometheus | `{}` | +| `metrics.serviceMonitor.podTargetLabels` | Labels from the Kubernetes pod to be transferred to the created metrics | `[]` | +| `metrics.serviceMonitor.sampleLimit` | Limit of how many samples should be scraped from every Pod | `false` | +| `metrics.serviceMonitor.targetLimit` | Limit of how many targets should be scraped | `false` | +| `metrics.serviceMonitor.additionalEndpoints` | Additional endpoints to scrape (e.g sentinel) | `[]` | +| `metrics.podMonitor.port` | the pod port to scrape metrics from | `metrics` | +| `metrics.podMonitor.enabled` | Create PodMonitor resource(s) for scraping metrics using PrometheusOperator | `false` | +| `metrics.podMonitor.namespace` | The namespace in which the PodMonitor will be created | `""` | +| `metrics.podMonitor.interval` | The interval at which metrics should be scraped | `30s` | +| `metrics.podMonitor.scrapeTimeout` | The timeout after which the scrape is ended | `""` | +| `metrics.podMonitor.relabelings` | Metrics RelabelConfigs to apply to samples before scraping. | `[]` | +| `metrics.podMonitor.metricRelabelings` | Metrics RelabelConfigs to apply to samples before ingestion. | `[]` | +| `metrics.podMonitor.honorLabels` | Specify honorLabels parameter to add the scrape endpoint | `false` | +| `metrics.podMonitor.additionalLabels` | Additional labels that can be used so PodMonitor resource(s) can be discovered by Prometheus | `{}` | +| `metrics.podMonitor.podTargetLabels` | Labels from the Kubernetes pod to be transferred to the created metrics | `[]` | +| `metrics.podMonitor.sampleLimit` | Limit of how many samples should be scraped from every Pod | `false` | +| `metrics.podMonitor.targetLimit` | Limit of how many targets should be scraped | `false` | +| `metrics.podMonitor.additionalEndpoints` | Additional endpoints to scrape (e.g sentinel) | `[]` | +| `metrics.prometheusRule.enabled` | Create a custom prometheusRule Resource for scraping metrics using PrometheusOperator | `false` | +| `metrics.prometheusRule.namespace` | The namespace in which the prometheusRule will be created | `""` | +| `metrics.prometheusRule.additionalLabels` | Additional labels for the prometheusRule | `{}` | +| `metrics.prometheusRule.rules` | Custom Prometheus rules | `[]` | + +### Init Container Parameters + +| Name | Description | Value | +| ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | +| `volumePermissions.enabled` | Enable init container that changes the owner/group of the PV mount point to `runAsUser:fsGroup` | `false` | +| `volumePermissions.image.registry` | OS Shell + Utility image registry | `REGISTRY_NAME` | +| `volumePermissions.image.repository` | OS Shell + Utility image repository | `REPOSITORY_NAME/os-shell` | +| `volumePermissions.image.digest` | OS Shell + Utility image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `volumePermissions.image.pullPolicy` | OS Shell + Utility image pull policy | `IfNotPresent` | +| `volumePermissions.image.pullSecrets` | OS Shell + Utility image pull secrets | `[]` | +| `volumePermissions.resourcesPreset` | Set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). This is ignored if volumePermissions.resources is set (volumePermissions.resources is recommended for production). | `nano` | +| `volumePermissions.resources` | Set container requests and limits for different resources like CPU or memory (essential for production workloads) | `{}` | +| `volumePermissions.containerSecurityContext.seLinuxOptions` | Set SELinux options in container | `{}` | +| `volumePermissions.containerSecurityContext.runAsUser` | Set init container's Security Context runAsUser | `0` | +| `volumePermissions.extraEnvVars` | Array with extra environment variables to add to volume permissions init container. | `[]` | +| `kubectl.image.registry` | Kubectl image registry | `REGISTRY_NAME` | +| `kubectl.image.repository` | Kubectl image repository | `REPOSITORY_NAME/kubectl` | +| `kubectl.image.digest` | Kubectl image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `kubectl.image.pullPolicy` | Kubectl image pull policy | `IfNotPresent` | +| `kubectl.image.pullSecrets` | Kubectl pull secrets | `[]` | +| `kubectl.command` | kubectl command to execute | `["/opt/bitnami/scripts/kubectl-scripts/update-master-label.sh"]` | +| `kubectl.containerSecurityContext.enabled` | Enabled kubectl containers' Security Context | `true` | +| `kubectl.containerSecurityContext.seLinuxOptions` | Set SELinux options in container | `{}` | +| `kubectl.containerSecurityContext.runAsUser` | Set kubectl containers' Security Context runAsUser | `1001` | +| `kubectl.containerSecurityContext.runAsGroup` | Set kubectl containers' Security Context runAsGroup | `1001` | +| `kubectl.containerSecurityContext.runAsNonRoot` | Set kubectl containers' Security Context runAsNonRoot | `true` | +| `kubectl.containerSecurityContext.allowPrivilegeEscalation` | Set kubectl containers' Security Context allowPrivilegeEscalation | `false` | +| `kubectl.containerSecurityContext.readOnlyRootFilesystem` | Set container's Security Context read-only root filesystem | `true` | +| `kubectl.containerSecurityContext.seccompProfile.type` | Set kubectl containers' Security Context seccompProfile | `RuntimeDefault` | +| `kubectl.containerSecurityContext.capabilities.drop` | Set kubectl containers' Security Context capabilities to drop | `["ALL"]` | +| `kubectl.resources.limits` | The resources limits for the kubectl containers | `{}` | +| `kubectl.resources.requests` | The requested resources for the kubectl containers | `{}` | +| `sysctl.enabled` | Enable init container to modify Kernel settings | `false` | +| `sysctl.image.registry` | OS Shell + Utility image registry | `REGISTRY_NAME` | +| `sysctl.image.repository` | OS Shell + Utility image repository | `REPOSITORY_NAME/os-shell` | +| `sysctl.image.digest` | OS Shell + Utility image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `sysctl.image.pullPolicy` | OS Shell + Utility image pull policy | `IfNotPresent` | +| `sysctl.image.pullSecrets` | OS Shell + Utility image pull secrets | `[]` | +| `sysctl.command` | Override default init-sysctl container command (useful when using custom images) | `[]` | +| `sysctl.mountHostSys` | Mount the host `/sys` folder to `/host-sys` | `false` | +| `sysctl.resourcesPreset` | Set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). This is ignored if sysctl.resources is set (sysctl.resources is recommended for production). | `nano` | +| `sysctl.resources` | Set container requests and limits for different resources like CPU or memory (essential for production workloads) | `{}` | + +### useExternalDNS Parameters + +| Name | Description | Value | +| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | +| `useExternalDNS.enabled` | Enable various syntax that would enable external-dns to work. Note this requires a working installation of `external-dns` to be usable. | `false` | +| `useExternalDNS.additionalAnnotations` | Extra annotations to be utilized when `external-dns` is enabled. | `{}` | +| `useExternalDNS.annotationKey` | The annotation key utilized when `external-dns` is enabled. Setting this to `false` will disable annotations. | `external-dns.alpha.kubernetes.io/` | +| `useExternalDNS.suffix` | The DNS suffix utilized when `external-dns` is enabled. Note that we prepend the suffix with the full name of the release. | `""` | + +Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, + +```console +helm install my-release \ + --set auth.password=secretpassword \ + oci://REGISTRY_NAME/REPOSITORY_NAME/redis +``` + +> Note: You need to substitute the placeholders `REGISTRY_NAME` and `REPOSITORY_NAME` with a reference to your Helm chart registry and repository. For example, in the case of Bitnami, you need to use `REGISTRY_NAME=registry-1.docker.io` and `REPOSITORY_NAME=bitnamicharts`. + +The above command sets the Redis® server password to `secretpassword`. + +> NOTE: Once this chart is deployed, it is not possible to change the application's access credentials, such as usernames or passwords, using Helm. To change these application credentials after deployment, delete any persistent volumes (PVs) used by the chart and re-deploy it, or use the application's built-in administrative tools if available. + +Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example, + +```console +helm install my-release -f values.yaml oci://REGISTRY_NAME/REPOSITORY_NAME/redis +``` + +> Note: You need to substitute the placeholders `REGISTRY_NAME` and `REPOSITORY_NAME` with a reference to your Helm chart registry and repository. For example, in the case of Bitnami, you need to use `REGISTRY_NAME=registry-1.docker.io` and `REPOSITORY_NAME=bitnamicharts`. +> **Tip**: You can use the default [values.yaml](https://github.com/bitnami/charts/tree/main/bitnami/redis/values.yaml) + +## Troubleshooting + +Find more information about how to deal with common errors related to Bitnami's Helm charts in [this troubleshooting guide](https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues). + +## Upgrading + +### To 20.5.0 + +This version introduces image verification for security purposes. To disable it, set `global.security.allowInsecureImages` to `true`. More details at [GitHub issue](https://github.com/bitnami/charts/issues/30850). + +A major chart version change (like v1.2.3 -> v2.0.0) indicates that there is an incompatible breaking change needing manual actions. + +### RDB compatibility + +It's common to have RDB format changes across Redis® releases where we see backward compatibility but no forward compatibility. For example, v7.0 can load an RDB created by v6.2 , but the opposite is not true. +When that's the case, the rolling update can cause replicas to temporarily stop synchronizing while they are running a lower version than master. +For example, on a rolling update `master-0` and `replica-2` are updated first from version v6.2 to v7.0; `replica-0` and `replica-1` won't be able to start a full sync with `master-0` because they are still running v6.2 and can't support the RDB format from version 7.0 that master is now using. +This issue can be mitigated by splitting the upgrade into two stages: one for all replicas and another for any master. + +- Stage 1 (replicas only, as there's no master with an ordinal higher than 99): +`helm upgrade oci://REGISTRY_NAME/REPOSITORY_NAME/redis --set master.updateStrategy.rollingUpdate.partition=99` +- Stage 2 (anything else that is not up to date, in this case only master): +`helm upgrade oci://REGISTRY_NAME/REPOSITORY_NAME/redis` + +### To 20.0.0 + +This major version updates the Redis® docker image version used from `7.2` to `7.4`, the new stable version. There are no major changes in the chart, but we recommend checking the [Redis® 7.4 release notes](https://raw.githubusercontent.com/redis/redis/7.4/00-RELEASENOTES) before upgrading. + +### To 19.0.0 + +This major bump changes the following security defaults: + +- `runAsGroup` is changed from `0` to `1001` +- `readOnlyRootFilesystem` is set to `true` +- `resourcesPreset` is changed from `none` to the minimum size working in our test suites (NOTE: `resourcesPreset` is not meant for production usage, but `resources` adapted to your use case). +- `global.compatibility.openshift.adaptSecurityContext` is changed from `disabled` to `auto`. + +This could potentially break any customization or init scripts used in your deployment. If this is the case, change the default values to the previous ones. + +### To 18.0.0 + +This major version updates the Redis® docker image version used from `7.0` to `7.2`, the new stable version. There are no major changes in the chart, but we recommend checking the [Redis® 7.2 release notes](https://raw.githubusercontent.com/redis/redis/7.2/00-RELEASENOTES) before upgrading. + +NOTE: Due to an error in our release process, versions higher or equal than 17.15.4 already use 7.2 by default. + +### To 17.0.0 + +This major version updates the Redis® docker image version used from `6.2` to `7.0`, the new stable version. There are no major changes in the chart, but we recommend checking the [Redis® 7.0 release notes](https://raw.githubusercontent.com/redis/redis/7.0/00-RELEASENOTES) before upgrading. + +### To 16.0.0 + +This major release renames several values in this chart and adds missing features, in order to be inline with the rest of assets in the Bitnami charts repository. + +Affected values: + +- `master.service.port` renamed as `master.service.ports.redis`. +- `master.service.nodePort` renamed as `master.service.nodePorts.redis`. +- `replica.service.port` renamed as `replica.service.ports.redis`. +- `replica.service.nodePort` renamed as `replica.service.nodePorts.redis`. +- `sentinel.service.port` renamed as `sentinel.service.ports.redis`. +- `sentinel.service.sentinelPort` renamed as `sentinel.service.ports.sentinel`. +- `master.containerPort` renamed as `master.containerPorts.redis`. +- `replica.containerPort` renamed as `replica.containerPorts.redis`. +- `sentinel.containerPort` renamed as `sentinel.containerPorts.sentinel`. +- `master.spreadConstraints` renamed as `master.topologySpreadConstraints` +- `replica.spreadConstraints` renamed as `replica.topologySpreadConstraints` + +### To 15.0.0 + +The parameter to enable the usage of StaticIDs was removed. The behavior is to [always use StaticIDs](https://github.com/bitnami/charts/pull/7278). + +### To 14.8.0 + +The Redis® sentinel exporter was removed in this version because the upstream project was deprecated. The regular Redis® exporter is included in the sentinel scenario as usual. + +### To 14.0.0 + +- Several parameters were renamed or disappeared in favor of new ones on this major version: + - The term *slave* has been replaced by the term *replica*. Therefore, parameters prefixed with `slave` are now prefixed with `replicas`. + - Credentials parameter are reorganized under the `auth` parameter. + - `cluster.enabled` parameter is deprecated in favor of `architecture` parameter that accepts two values: `standalone` and `replication`. + - `securityContext.*` is deprecated in favor of `XXX.podSecurityContext` and `XXX.containerSecurityContext`. + - `sentinel.metrics.*` parameters are deprecated in favor of `metrics.sentinel.*` ones. +- New parameters to add custom command, environment variables, sidecars, init containers, etc. were added. +- Chart labels were adapted to follow the [Helm charts standard labels](https://helm.sh/docs/chart_best_practices/labels/#standard-labels). +- values.yaml metadata was adapted to follow the format supported by [Readme Generator for Helm](https://github.com/bitnami/readme-generator-for-helm). + +Consequences: + +Backwards compatibility is not guaranteed. To upgrade to `14.0.0`, install a new release of the Redis® chart, and migrate the data from your previous release. You have 2 alternatives to do so: + +- Create a backup of the database, and restore it on the new release as explained in the [Backup and restore](#backup-and-restore) section. +- Reuse the PVC used to hold the master data on your previous release. To do so, use the `master.persistence.existingClaim` parameter. The following example assumes that the release name is `redis`: + +```console +helm install redis oci://REGISTRY_NAME/REPOSITORY_NAME/redis --set auth.password=[PASSWORD] --set master.persistence.existingClaim=[EXISTING_PVC] +``` + +> Note: You need to substitute the placeholders `REGISTRY_NAME` and `REPOSITORY_NAME` with a reference to your Helm chart registry and repository. For example, in the case of Bitnami, you need to use `REGISTRY_NAME=registry-1.docker.io` and `REPOSITORY_NAME=bitnamicharts`. + +| Note: you need to substitute the placeholder *[EXISTING_PVC]* with the name of the PVC used on your previous release, and *[PASSWORD]* with the password used in your previous release. + +### To 13.0.0 + +This major version updates the Redis® docker image version used from `6.0` to `6.2`, the new stable version. There are no major changes in the chart and there shouldn't be any breaking changes in it as `6.2` is basically a stricter superset of `6.0`. For more information, please refer to [Redis® 6.2 release notes](https://raw.githubusercontent.com/redis/redis/6.2/00-RELEASENOTES). + +### To 12.3.0 + +This version also introduces `bitnami/common`, a [library chart](https://helm.sh/docs/topics/library_charts/#helm) as a dependency. More documentation about this new utility could be found [here](https://github.com/bitnami/charts/tree/main/bitnami/common#bitnami-common-library-chart). Please, make sure that you have updated the chart dependencies before executing any upgrade. + +### To 12.0.0 + +[On November 13, 2020, Helm v2 support was formally finished](https://github.com/helm/charts#status-of-the-project), this major version is the result of the required changes applied to the Helm Chart to be able to incorporate the different features added in Helm v3 and to be consistent with the Helm project itself regarding the Helm v2 EOL. + +#### What changes were introduced in this major version? + +- Previous versions of this Helm Chart use `apiVersion: v1` (installable by both Helm 2 and 3), this Helm Chart was updated to `apiVersion: v2` (installable by Helm 3 only). [Here](https://helm.sh/docs/topics/charts/#the-apiversion-field) you can find more information about the `apiVersion` field. +- The different fields present in the *Chart.yaml* file has been ordered alphabetically in a homogeneous way for all the Bitnami Helm Charts + +#### Considerations when upgrading to this version + +- If you want to upgrade to this version from a previous one installed with Helm v3, you shouldn't face any issues +- If you want to upgrade to this version using Helm v2, this scenario is not supported as this version doesn't support Helm v2 anymore +- If you installed the previous version with Helm v2 and wants to upgrade to this version with Helm v3, please refer to the [official Helm documentation](https://helm.sh/docs/topics/v2_v3_migration/#migration-use-cases) about migrating from Helm v2 to v3 + +#### Useful links + +- +- +- + +### To 11.0.0 + +When using sentinel, a new statefulset called `-node` was introduced. This will break upgrading from a previous version where the statefulsets are called master and slave. Hence the PVC will not match the new naming and won't be reused. If you want to keep your data, you will need to perform a backup and then a restore the data in this new version. + +When deployed with sentinel enabled, only a group of nodes is deployed and the master/slave role is handled in the group. To avoid breaking the compatibility, the settings for this nodes are given through the `slave.xxxx` parameters in `values.yaml` + +### To 10.0.0 + +For releases with `usePassword: true`, the value `sentinel.usePassword` controls whether the password authentication also applies to the sentinel port. This defaults to `true` for a secure configuration, however it is possible to disable to account for the following cases: + +- Using a version of redis-sentinel prior to `5.0.1` where the authentication feature was introduced. +- Where redis clients need to be updated to support sentinel authentication. + +If using a master/slave topology, or with `usePassword: false`, no action is required. + +### To 9.0.0 + +The metrics exporter has been changed from a separate deployment to a sidecar container, due to the latest changes in the Redis® exporter code. Check the [official page](https://github.com/oliver006/redis_exporter/) for more information. The metrics container image was changed from oliver006/redis_exporter to bitnami/redis-exporter (Bitnami's maintained package of oliver006/redis_exporter). + +### To 8.0.18 + +For releases with `metrics.enabled: true` the default tag for the exporter image is now `v1.x.x`. This introduces many changes including metrics names. You'll want to use [this dashboard](https://github.com/oliver006/redis_exporter/blob/master/contrib/grafana_prometheus_redis_dashboard.json) now. Please see the [redis_exporter github page](https://github.com/oliver006/redis_exporter#upgrading-from-0x-to-1x) for more details. + +### To 7.0.0 + +This version causes a change in the Redis® Master StatefulSet definition, so the command helm upgrade would not work out of the box. As an alternative, one of the following could be done: + +- Recommended: Create a clone of the Redis® Master PVC (for example, using projects like [this one](https://github.com/edseymour/pvc-transfer)). Then launch a fresh release reusing this cloned PVC. + +```console +helm install my-release oci://REGISTRY_NAME/REPOSITORY_NAME/redis --set persistence.existingClaim= +``` + +> Note: You need to substitute the placeholders `REGISTRY_NAME` and `REPOSITORY_NAME` with a reference to your Helm chart registry and repository. For example, in the case of Bitnami, you need to use `REGISTRY_NAME=registry-1.docker.io` and `REPOSITORY_NAME=bitnamicharts`. + +- Alternative (not recommended, do at your own risk): `helm delete --purge` does not remove the PVC assigned to the Redis® Master StatefulSet. As a consequence, the following commands can be done to upgrade the release + +```console +helm delete --purge +helm install oci://REGISTRY_NAME/REPOSITORY_NAME/redis +``` + +> Note: You need to substitute the placeholders `REGISTRY_NAME` and `REPOSITORY_NAME` with a reference to your Helm chart registry and repository. For example, in the case of Bitnami, you need to use `REGISTRY_NAME=registry-1.docker.io` and `REPOSITORY_NAME=bitnamicharts`. + +Previous versions of the chart were not using persistence in the slaves, so this upgrade would add it to them. Another important change is that no values are inherited from master to slaves. For example, in 6.0.0 `slaves.readinessProbe.periodSeconds`, if empty, would be set to `master.readinessProbe.periodSeconds`. This approach lacked transparency and was difficult to maintain. From now on, all the slave parameters must be configured just as it is done with the masters. + +Some values have changed as well: + +- `master.port` and `slave.port` have been changed to `redisPort` (same value for both master and slaves) +- `master.securityContext` and `slave.securityContext` have been changed to `securityContext`(same values for both master and slaves) + +By default, the upgrade will not change the cluster topology. In case you want to use Redis® Sentinel, you must explicitly set `sentinel.enabled` to `true`. + +### To 6.0.0 + +Previous versions of the chart were using an init-container to change the permissions of the volumes. This was done in case the `securityContext` directive in the template was not enough for that (for example, with cephFS). In this new version of the chart, this container is disabled by default (which should not affect most of the deployments). If your installation still requires that init container, execute `helm upgrade` with the `--set volumePermissions.enabled=true`. + +### To 5.0.0 + +The default image in this release may be switched out for any image containing the `redis-server` +and `redis-cli` binaries. If `redis-server` is not the default image ENTRYPOINT, `master.command` +must be specified. + +#### Breaking changes + +- `master.args` and `slave.args` are removed. Use `master.command` or `slave.command` instead in order to override the image entrypoint, or `master.extraFlags` to pass additional flags to `redis-server`. +- `disableCommands` is now interpreted as an array of strings instead of a string of comma separated values. +- `master.persistence.path` now defaults to `/data`. + +### To 4.0.0 + +This version removes the `chart` label from the `spec.selector.matchLabels` +which is immutable since `StatefulSet apps/v1beta2`. It has been inadvertently +added, causing any subsequent upgrade to fail. See . + +It also fixes where a deployment `extensions/v1beta1` can not be upgraded if `spec.selector` is not explicitly set. + +Finally, it fixes by removing mutable labels in `spec.VolumeClaimTemplate.metadata.labels` so that it is upgradable. + +In order to upgrade, delete the Redis® StatefulSet before upgrading: + +```console +kubectl delete statefulsets.apps --cascade=false my-release-redis-master +``` + +And edit the Redis® slave (and metrics if enabled) deployment: + +```console +kubectl patch deployments my-release-redis-slave --type=json -p='[{"op": "remove", "path": "/spec/selector/matchLabels/chart"}]' +kubectl patch deployments my-release-redis-metrics --type=json -p='[{"op": "remove", "path": "/spec/selector/matchLabels/chart"}]' +``` + +## License + +Copyright © 2024 Broadcom. The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/charts/open-webui/charts/redis/charts/common/.helmignore b/charts/open-webui/charts/redis/charts/common/.helmignore new file mode 100644 index 0000000..d0e1084 --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/.helmignore @@ -0,0 +1,26 @@ +# 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 +.vscode/ +# img folder +img/ +# Changelog +CHANGELOG.md diff --git a/charts/open-webui/charts/redis/charts/common/Chart.yaml b/charts/open-webui/charts/redis/charts/common/Chart.yaml new file mode 100644 index 0000000..4ac14bc --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/Chart.yaml @@ -0,0 +1,23 @@ +annotations: + category: Infrastructure + licenses: Apache-2.0 +apiVersion: v2 +appVersion: 2.29.0 +description: A Library Helm Chart for grouping common logic between bitnami charts. + This chart is not deployable by itself. +home: https://bitnami.com +icon: https://bitnami.com/downloads/logos/bitnami-mark.png +keywords: +- common +- helper +- template +- function +- bitnami +maintainers: +- name: Broadcom, Inc. All Rights Reserved. + url: https://github.com/bitnami/charts +name: common +sources: +- https://github.com/bitnami/charts/tree/main/bitnami/common +type: library +version: 2.29.0 diff --git a/charts/open-webui/charts/redis/charts/common/README.md b/charts/open-webui/charts/redis/charts/common/README.md new file mode 100644 index 0000000..3943ed0 --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/README.md @@ -0,0 +1,235 @@ +# Bitnami Common Library Chart + +A [Helm Library Chart](https://helm.sh/docs/topics/library_charts/#helm) for grouping common logic between Bitnami charts. + +## TL;DR + +```yaml +dependencies: + - name: common + version: 2.x.x + repository: oci://registry-1.docker.io/bitnamicharts +``` + +```console +helm dependency update +``` + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "common.names.fullname" . }} +data: + myvalue: "Hello World" +``` + +Looking to use our applications in production? Try [VMware Tanzu Application Catalog](https://bitnami.com/enterprise), the commercial edition of the Bitnami catalog. + +## Introduction + +This chart provides a common template helpers which can be used to develop new charts using [Helm](https://helm.sh) package manager. + +Bitnami charts can be used with [Kubeapps](https://kubeapps.dev/) for deployment and management of Helm Charts in clusters. + +## Prerequisites + +- Kubernetes 1.23+ +- Helm 3.8.0+ + +## Parameters + +## Special input schemas + +### ImageRoot + +```yaml +registry: + type: string + description: Docker registry where the image is located + example: docker.io + +repository: + type: string + description: Repository and image name + example: bitnami/nginx + +tag: + type: string + description: image tag + example: 1.16.1-debian-10-r63 + +pullPolicy: + type: string + description: Specify a imagePullPolicy.' + +pullSecrets: + type: array + items: + type: string + description: Optionally specify an array of imagePullSecrets (evaluated as templates). + +debug: + type: boolean + description: Set to true if you would like to see extra information on logs + example: false + +## An instance would be: +# registry: docker.io +# repository: bitnami/nginx +# tag: 1.16.1-debian-10-r63 +# pullPolicy: IfNotPresent +# debug: false +``` + +### Persistence + +```yaml +enabled: + type: boolean + description: Whether enable persistence. + example: true + +storageClass: + type: string + description: Ghost data Persistent Volume Storage Class, If set to "-", storageClassName: "" which disables dynamic provisioning. + example: "-" + +accessMode: + type: string + description: Access mode for the Persistent Volume Storage. + example: ReadWriteOnce + +size: + type: string + description: Size the Persistent Volume Storage. + example: 8Gi + +path: + type: string + description: Path to be persisted. + example: /bitnami + +## An instance would be: +# enabled: true +# storageClass: "-" +# accessMode: ReadWriteOnce +# size: 8Gi +# path: /bitnami +``` + +### ExistingSecret + +```yaml +name: + type: string + description: Name of the existing secret. + example: mySecret +keyMapping: + description: Mapping between the expected key name and the name of the key in the existing secret. + type: object + +## An instance would be: +# name: mySecret +# keyMapping: +# password: myPasswordKey +``` + +#### Example of use + +When we store sensitive data for a deployment in a secret, some times we want to give to users the possibility of using theirs existing secrets. + +```yaml +# templates/secret.yaml +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "common.names.fullname" . }} + labels: + app: {{ include "common.names.fullname" . }} +type: Opaque +data: + password: {{ .Values.password | b64enc | quote }} + +# templates/dpl.yaml +--- +... + env: + - name: PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "common.secrets.name" (dict "existingSecret" .Values.existingSecret "context" $) }} + key: {{ include "common.secrets.key" (dict "existingSecret" .Values.existingSecret "key" "password") }} +... + +# values.yaml +--- +name: mySecret +keyMapping: + password: myPasswordKey +``` + +### ValidateValue + +#### NOTES.txt + +```console +{{- $validateValueConf00 := (dict "valueKey" "path.to.value00" "secret" "secretName" "field" "password-00") -}} +{{- $validateValueConf01 := (dict "valueKey" "path.to.value01" "secret" "secretName" "field" "password-01") -}} + +{{ include "common.validations.values.multiple.empty" (dict "required" (list $validateValueConf00 $validateValueConf01) "context" $) }} +``` + +If we force those values to be empty we will see some alerts + +```console +helm install test mychart --set path.to.value00="",path.to.value01="" + 'path.to.value00' must not be empty, please add '--set path.to.value00=$PASSWORD_00' to the command. To get the current value: + + export PASSWORD_00=$(kubectl get secret --namespace default secretName -o jsonpath="{.data.password-00}" | base64 -d) + + 'path.to.value01' must not be empty, please add '--set path.to.value01=$PASSWORD_01' to the command. To get the current value: + + export PASSWORD_01=$(kubectl get secret --namespace default secretName -o jsonpath="{.data.password-01}" | base64 -d) +``` + +## Upgrading + +### To 1.0.0 + +[On November 13, 2020, Helm v2 support was formally finished](https://github.com/helm/charts#status-of-the-project), this major version is the result of the required changes applied to the Helm Chart to be able to incorporate the different features added in Helm v3 and to be consistent with the Helm project itself regarding the Helm v2 EOL. + +#### What changes were introduced in this major version? + +- Previous versions of this Helm Chart use `apiVersion: v1` (installable by both Helm 2 and 3), this Helm Chart was updated to `apiVersion: v2` (installable by Helm 3 only). [Here](https://helm.sh/docs/topics/charts/#the-apiversion-field) you can find more information about the `apiVersion` field. +- Use `type: library`. [Here](https://v3.helm.sh/docs/faq/#library-chart-support) you can find more information. +- The different fields present in the *Chart.yaml* file has been ordered alphabetically in a homogeneous way for all the Bitnami Helm Charts + +#### Considerations when upgrading to this version + +- If you want to upgrade to this version from a previous one installed with Helm v3, you shouldn't face any issues +- If you want to upgrade to this version using Helm v2, this scenario is not supported as this version doesn't support Helm v2 anymore +- If you installed the previous version with Helm v2 and wants to upgrade to this version with Helm v3, please refer to the [official Helm documentation](https://helm.sh/docs/topics/v2_v3_migration/#migration-use-cases) about migrating from Helm v2 to v3 + +#### Useful links + +- +- +- + +## License + +Copyright © 2024 Broadcom. The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/charts/open-webui/charts/redis/charts/common/templates/_affinities.tpl b/charts/open-webui/charts/redis/charts/common/templates/_affinities.tpl new file mode 100644 index 0000000..d387dbe --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/templates/_affinities.tpl @@ -0,0 +1,155 @@ +{{/* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* vim: set filetype=mustache: */}} + +{{/* +Return a soft nodeAffinity definition +{{ include "common.affinities.nodes.soft" (dict "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.nodes.soft" -}} +preferredDuringSchedulingIgnoredDuringExecution: + - preference: + matchExpressions: + - key: {{ .key }} + operator: In + values: + {{- range .values }} + - {{ . | quote }} + {{- end }} + weight: 1 +{{- end -}} + +{{/* +Return a hard nodeAffinity definition +{{ include "common.affinities.nodes.hard" (dict "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.nodes.hard" -}} +requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: {{ .key }} + operator: In + values: + {{- range .values }} + - {{ . | quote }} + {{- end }} +{{- end -}} + +{{/* +Return a nodeAffinity definition +{{ include "common.affinities.nodes" (dict "type" "soft" "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.nodes" -}} + {{- if eq .type "soft" }} + {{- include "common.affinities.nodes.soft" . -}} + {{- else if eq .type "hard" }} + {{- include "common.affinities.nodes.hard" . -}} + {{- end -}} +{{- end -}} + +{{/* +Return a topologyKey definition +{{ include "common.affinities.topologyKey" (dict "topologyKey" "BAR") -}} +*/}} +{{- define "common.affinities.topologyKey" -}} +{{ .topologyKey | default "kubernetes.io/hostname" -}} +{{- end -}} + +{{/* +Return a soft podAffinity/podAntiAffinity definition +{{ include "common.affinities.pods.soft" (dict "component" "FOO" "customLabels" .Values.podLabels "extraMatchLabels" .Values.extraMatchLabels "topologyKey" "BAR" "extraPodAffinityTerms" .Values.extraPodAffinityTerms "extraNamespaces" (list "namespace1" "namespace2") "context" $) -}} +*/}} +{{- define "common.affinities.pods.soft" -}} +{{- $component := default "" .component -}} +{{- $customLabels := default (dict) .customLabels -}} +{{- $extraMatchLabels := default (dict) .extraMatchLabels -}} +{{- $extraPodAffinityTerms := default (list) .extraPodAffinityTerms -}} +{{- $extraNamespaces := default (list) .extraNamespaces -}} +preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: {{- (include "common.labels.matchLabels" ( dict "customLabels" $customLabels "context" .context )) | nindent 10 }} + {{- if not (empty $component) }} + {{ printf "app.kubernetes.io/component: %s" $component }} + {{- end }} + {{- range $key, $value := $extraMatchLabels }} + {{ $key }}: {{ $value | quote }} + {{- end }} + {{- if $extraNamespaces }} + namespaces: + - {{ .context.Release.Namespace }} + {{- with $extraNamespaces }} + {{ include "common.tplvalues.render" (dict "value" . "context" $) | nindent 8 }} + {{- end }} + {{- end }} + topologyKey: {{ include "common.affinities.topologyKey" (dict "topologyKey" .topologyKey) }} + weight: 1 + {{- range $extraPodAffinityTerms }} + - podAffinityTerm: + labelSelector: + matchLabels: {{- (include "common.labels.matchLabels" ( dict "customLabels" $customLabels "context" $.context )) | nindent 10 }} + {{- if not (empty $component) }} + {{ printf "app.kubernetes.io/component: %s" $component }} + {{- end }} + {{- range $key, $value := .extraMatchLabels }} + {{ $key }}: {{ $value | quote }} + {{- end }} + topologyKey: {{ include "common.affinities.topologyKey" (dict "topologyKey" .topologyKey) }} + weight: {{ .weight | default 1 -}} + {{- end -}} +{{- end -}} + +{{/* +Return a hard podAffinity/podAntiAffinity definition +{{ include "common.affinities.pods.hard" (dict "component" "FOO" "customLabels" .Values.podLabels "extraMatchLabels" .Values.extraMatchLabels "topologyKey" "BAR" "extraPodAffinityTerms" .Values.extraPodAffinityTerms "extraNamespaces" (list "namespace1" "namespace2") "context" $) -}} +*/}} +{{- define "common.affinities.pods.hard" -}} +{{- $component := default "" .component -}} +{{- $customLabels := default (dict) .customLabels -}} +{{- $extraMatchLabels := default (dict) .extraMatchLabels -}} +{{- $extraPodAffinityTerms := default (list) .extraPodAffinityTerms -}} +{{- $extraNamespaces := default (list) .extraNamespaces -}} +requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: {{- (include "common.labels.matchLabels" ( dict "customLabels" $customLabels "context" .context )) | nindent 8 }} + {{- if not (empty $component) }} + {{ printf "app.kubernetes.io/component: %s" $component }} + {{- end }} + {{- range $key, $value := $extraMatchLabels }} + {{ $key }}: {{ $value | quote }} + {{- end }} + {{- if $extraNamespaces }} + namespaces: + - {{ .context.Release.Namespace }} + {{- with $extraNamespaces }} + {{ include "common.tplvalues.render" (dict "value" . "context" $) | nindent 8 }} + {{- end }} + {{- end }} + topologyKey: {{ include "common.affinities.topologyKey" (dict "topologyKey" .topologyKey) }} + {{- range $extraPodAffinityTerms }} + - labelSelector: + matchLabels: {{- (include "common.labels.matchLabels" ( dict "customLabels" $customLabels "context" $.context )) | nindent 8 }} + {{- if not (empty $component) }} + {{ printf "app.kubernetes.io/component: %s" $component }} + {{- end }} + {{- range $key, $value := .extraMatchLabels }} + {{ $key }}: {{ $value | quote }} + {{- end }} + topologyKey: {{ include "common.affinities.topologyKey" (dict "topologyKey" .topologyKey) }} + {{- end -}} +{{- end -}} + +{{/* +Return a podAffinity/podAntiAffinity definition +{{ include "common.affinities.pods" (dict "type" "soft" "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.pods" -}} + {{- if eq .type "soft" }} + {{- include "common.affinities.pods.soft" . -}} + {{- else if eq .type "hard" }} + {{- include "common.affinities.pods.hard" . -}} + {{- end -}} +{{- end -}} diff --git a/charts/open-webui/charts/redis/charts/common/templates/_capabilities.tpl b/charts/open-webui/charts/redis/charts/common/templates/_capabilities.tpl new file mode 100644 index 0000000..7738f73 --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/templates/_capabilities.tpl @@ -0,0 +1,239 @@ +{{/* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* vim: set filetype=mustache: */}} + +{{/* +Return the target Kubernetes version +*/}} +{{- define "common.capabilities.kubeVersion" -}} +{{- default (default .Capabilities.KubeVersion.Version .Values.kubeVersion) ((.Values.global).kubeVersion) -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for poddisruptionbudget. +*/}} +{{- define "common.capabilities.policy.apiVersion" -}} +{{- $kubeVersion := include "common.capabilities.kubeVersion" . -}} +{{- if and (not (empty $kubeVersion)) (semverCompare "<1.21-0" $kubeVersion) -}} +{{- print "policy/v1beta1" -}} +{{- else -}} +{{- print "policy/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for networkpolicy. +*/}} +{{- define "common.capabilities.networkPolicy.apiVersion" -}} +{{- $kubeVersion := include "common.capabilities.kubeVersion" . -}} +{{- if and (not (empty $kubeVersion)) (semverCompare "<1.7-0" $kubeVersion) -}} +{{- print "extensions/v1beta1" -}} +{{- else -}} +{{- print "networking.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for job. +*/}} +{{- define "common.capabilities.job.apiVersion" -}} +{{- $kubeVersion := include "common.capabilities.kubeVersion" . -}} +{{- if and (not (empty $kubeVersion)) (semverCompare "<1.21-0" $kubeVersion) -}} +{{- print "batch/v1beta1" -}} +{{- else -}} +{{- print "batch/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for cronjob. +*/}} +{{- define "common.capabilities.cronjob.apiVersion" -}} +{{- $kubeVersion := include "common.capabilities.kubeVersion" . -}} +{{- if and (not (empty $kubeVersion)) (semverCompare "<1.21-0" $kubeVersion) -}} +{{- print "batch/v1beta1" -}} +{{- else -}} +{{- print "batch/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for daemonset. +*/}} +{{- define "common.capabilities.daemonset.apiVersion" -}} +{{- $kubeVersion := include "common.capabilities.kubeVersion" . -}} +{{- if and (not (empty $kubeVersion)) (semverCompare "<1.14-0" $kubeVersion) -}} +{{- print "extensions/v1beta1" -}} +{{- else -}} +{{- print "apps/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for deployment. +*/}} +{{- define "common.capabilities.deployment.apiVersion" -}} +{{- $kubeVersion := include "common.capabilities.kubeVersion" . -}} +{{- if and (not (empty $kubeVersion)) (semverCompare "<1.14-0" $kubeVersion) -}} +{{- print "extensions/v1beta1" -}} +{{- else -}} +{{- print "apps/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for statefulset. +*/}} +{{- define "common.capabilities.statefulset.apiVersion" -}} +{{- $kubeVersion := include "common.capabilities.kubeVersion" . -}} +{{- if and (not (empty $kubeVersion)) (semverCompare "<1.14-0" $kubeVersion) -}} +{{- print "apps/v1beta1" -}} +{{- else -}} +{{- print "apps/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for ingress. +*/}} +{{- define "common.capabilities.ingress.apiVersion" -}} +{{- $kubeVersion := include "common.capabilities.kubeVersion" . -}} +{{- if (.Values.ingress).apiVersion -}} +{{- .Values.ingress.apiVersion -}} +{{- else if and (not (empty $kubeVersion)) (semverCompare "<1.14-0" $kubeVersion) -}} +{{- print "extensions/v1beta1" -}} +{{- else if and (not (empty $kubeVersion)) (semverCompare "<1.19-0" $kubeVersion) -}} +{{- print "networking.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "networking.k8s.io/v1" -}} +{{- end }} +{{- end -}} + +{{/* +Return the appropriate apiVersion for RBAC resources. +*/}} +{{- define "common.capabilities.rbac.apiVersion" -}} +{{- $kubeVersion := include "common.capabilities.kubeVersion" . -}} +{{- if and (not (empty $kubeVersion)) (semverCompare "<1.17-0" $kubeVersion) -}} +{{- print "rbac.authorization.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "rbac.authorization.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for CRDs. +*/}} +{{- define "common.capabilities.crd.apiVersion" -}} +{{- $kubeVersion := include "common.capabilities.kubeVersion" . -}} +{{- if and (not (empty $kubeVersion)) (semverCompare "<1.19-0" $kubeVersion) -}} +{{- print "apiextensions.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "apiextensions.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for APIService. +*/}} +{{- define "common.capabilities.apiService.apiVersion" -}} +{{- $kubeVersion := include "common.capabilities.kubeVersion" . -}} +{{- if and (not (empty $kubeVersion)) (semverCompare "<1.10-0" $kubeVersion) -}} +{{- print "apiregistration.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "apiregistration.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for Horizontal Pod Autoscaler. +*/}} +{{- define "common.capabilities.hpa.apiVersion" -}} +{{- $kubeVersion := include "common.capabilities.kubeVersion" .context -}} +{{- if and (not (empty $kubeVersion)) (semverCompare "<1.23-0" $kubeVersion) -}} +{{- if .beta2 -}} +{{- print "autoscaling/v2beta2" -}} +{{- else -}} +{{- print "autoscaling/v2beta1" -}} +{{- end -}} +{{- else -}} +{{- print "autoscaling/v2" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for Vertical Pod Autoscaler. +*/}} +{{- define "common.capabilities.vpa.apiVersion" -}} +{{- $kubeVersion := include "common.capabilities.kubeVersion" .context -}} +{{- if and (not (empty $kubeVersion)) (semverCompare "<1.11-0" $kubeVersion) -}} +{{- print "autoscaling/v1beta1" -}} +{{- else if and (not (empty $kubeVersion)) (semverCompare "<1.25-0" $kubeVersion) -}} +{{- print "autoscaling/v1beta2" -}} +{{- else -}} +{{- print "autoscaling/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Returns true if PodSecurityPolicy is supported +*/}} +{{- define "common.capabilities.psp.supported" -}} +{{- $kubeVersion := include "common.capabilities.kubeVersion" . -}} +{{- if or (empty $kubeVersion) (semverCompare "<1.25-0" $kubeVersion) -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Returns true if AdmissionConfiguration is supported +*/}} +{{- define "common.capabilities.admissionConfiguration.supported" -}} +{{- $kubeVersion := include "common.capabilities.kubeVersion" . -}} +{{- if or (empty $kubeVersion) (not (semverCompare "<1.23-0" $kubeVersion)) -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for AdmissionConfiguration. +*/}} +{{- define "common.capabilities.admissionConfiguration.apiVersion" -}} +{{- $kubeVersion := include "common.capabilities.kubeVersion" . -}} +{{- if and (not (empty $kubeVersion)) (semverCompare "<1.23-0" $kubeVersion) -}} +{{- print "apiserver.config.k8s.io/v1alpha1" -}} +{{- else if and (not (empty $kubeVersion)) (semverCompare "<1.25-0" $kubeVersion) -}} +{{- print "apiserver.config.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "apiserver.config.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for PodSecurityConfiguration. +*/}} +{{- define "common.capabilities.podSecurityConfiguration.apiVersion" -}} +{{- $kubeVersion := include "common.capabilities.kubeVersion" . -}} +{{- if and (not (empty $kubeVersion)) (semverCompare "<1.23-0" $kubeVersion) -}} +{{- print "pod-security.admission.config.k8s.io/v1alpha1" -}} +{{- else if and (not (empty $kubeVersion)) (semverCompare "<1.25-0" $kubeVersion) -}} +{{- print "pod-security.admission.config.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "pod-security.admission.config.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Returns true if the used Helm version is 3.3+. +A way to check the used Helm version was not introduced until version 3.3.0 with .Capabilities.HelmVersion, which contains an additional "{}}" structure. +This check is introduced as a regexMatch instead of {{ if .Capabilities.HelmVersion }} because checking for the key HelmVersion in <3.3 results in a "interface not found" error. +**To be removed when the catalog's minimun Helm version is 3.3** +*/}} +{{- define "common.capabilities.supportsHelmVersion" -}} +{{- if regexMatch "{(v[0-9])*[^}]*}}$" (.Capabilities | toString ) }} + {{- true -}} +{{- end -}} +{{- end -}} diff --git a/charts/open-webui/charts/redis/charts/common/templates/_compatibility.tpl b/charts/open-webui/charts/redis/charts/common/templates/_compatibility.tpl new file mode 100644 index 0000000..a61588d --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/templates/_compatibility.tpl @@ -0,0 +1,46 @@ +{{/* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* vim: set filetype=mustache: */}} + +{{/* +Return true if the detected platform is Openshift +Usage: +{{- include "common.compatibility.isOpenshift" . -}} +*/}} +{{- define "common.compatibility.isOpenshift" -}} +{{- if .Capabilities.APIVersions.Has "security.openshift.io/v1" -}} +{{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Render a compatible securityContext depending on the platform. By default it is maintained as it is. In other platforms like Openshift we remove default user/group values that do not work out of the box with the restricted-v1 SCC +Usage: +{{- include "common.compatibility.renderSecurityContext" (dict "secContext" .Values.containerSecurityContext "context" $) -}} +*/}} +{{- define "common.compatibility.renderSecurityContext" -}} +{{- $adaptedContext := .secContext -}} + +{{- if (((.context.Values.global).compatibility).openshift) -}} + {{- if or (eq .context.Values.global.compatibility.openshift.adaptSecurityContext "force") (and (eq .context.Values.global.compatibility.openshift.adaptSecurityContext "auto") (include "common.compatibility.isOpenshift" .context)) -}} + {{/* Remove incompatible user/group values that do not work in Openshift out of the box */}} + {{- $adaptedContext = omit $adaptedContext "fsGroup" "runAsUser" "runAsGroup" -}} + {{- if not .secContext.seLinuxOptions -}} + {{/* If it is an empty object, we remove it from the resulting context because it causes validation issues */}} + {{- $adaptedContext = omit $adaptedContext "seLinuxOptions" -}} + {{- end -}} + {{- end -}} +{{- end -}} +{{/* Remove empty seLinuxOptions object if global.compatibility.omitEmptySeLinuxOptions is set to true */}} +{{- if and (((.context.Values.global).compatibility).omitEmptySeLinuxOptions) (not .secContext.seLinuxOptions) -}} + {{- $adaptedContext = omit $adaptedContext "seLinuxOptions" -}} +{{- end -}} +{{/* Remove fields that are disregarded when running the container in privileged mode */}} +{{- if $adaptedContext.privileged -}} + {{- $adaptedContext = omit $adaptedContext "capabilities" "seLinuxOptions" -}} +{{- end -}} +{{- omit $adaptedContext "enabled" | toYaml -}} +{{- end -}} diff --git a/charts/open-webui/charts/redis/charts/common/templates/_errors.tpl b/charts/open-webui/charts/redis/charts/common/templates/_errors.tpl new file mode 100644 index 0000000..93f3ffc --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/templates/_errors.tpl @@ -0,0 +1,85 @@ +{{/* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* vim: set filetype=mustache: */}} +{{/* +Throw error when upgrading using empty passwords values that must not be empty. + +Usage: +{{- $validationError00 := include "common.validations.values.single.empty" (dict "valueKey" "path.to.password00" "secret" "secretName" "field" "password-00") -}} +{{- $validationError01 := include "common.validations.values.single.empty" (dict "valueKey" "path.to.password01" "secret" "secretName" "field" "password-01") -}} +{{ include "common.errors.upgrade.passwords.empty" (dict "validationErrors" (list $validationError00 $validationError01) "context" $) }} + +Required password params: + - validationErrors - String - Required. List of validation strings to be return, if it is empty it won't throw error. + - context - Context - Required. Parent context. +*/}} +{{- define "common.errors.upgrade.passwords.empty" -}} + {{- $validationErrors := join "" .validationErrors -}} + {{- if and $validationErrors .context.Release.IsUpgrade -}} + {{- $errorString := "\nPASSWORDS ERROR: You must provide your current passwords when upgrading the release." -}} + {{- $errorString = print $errorString "\n Note that even after reinstallation, old credentials may be needed as they may be kept in persistent volume claims." -}} + {{- $errorString = print $errorString "\n Further information can be obtained at https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues/#credential-errors-while-upgrading-chart-releases" -}} + {{- $errorString = print $errorString "\n%s" -}} + {{- printf $errorString $validationErrors | fail -}} + {{- end -}} +{{- end -}} + +{{/* +Throw error when original container images are replaced. +The error can be bypassed by setting the "global.security.allowInsecureImages" to true. In this case, +a warning message will be shown instead. + +Usage: +{{ include "common.errors.insecureImages" (dict "images" (list .Values.path.to.the.imageRoot) "context" $) }} +*/}} +{{- define "common.errors.insecureImages" -}} +{{- $relocatedImages := list -}} +{{- $replacedImages := list -}} +{{- $retaggedImages := list -}} +{{- $globalRegistry := ((.context.Values.global).imageRegistry) -}} +{{- $originalImages := .context.Chart.Annotations.images -}} +{{- range .images -}} + {{- $registryName := default .registry $globalRegistry -}} + {{- $fullImageNameNoTag := printf "%s/%s" $registryName .repository -}} + {{- $fullImageName := printf "%s:%s" $fullImageNameNoTag .tag -}} + {{- if not (contains $fullImageNameNoTag $originalImages) -}} + {{- if not (contains $registryName $originalImages) -}} + {{- $relocatedImages = append $relocatedImages $fullImageName -}} + {{- else if not (contains .repository $originalImages) -}} + {{- $replacedImages = append $replacedImages $fullImageName -}} + {{- end -}} + {{- end -}} + {{- if not (contains (printf "%s:%s" .repository .tag) $originalImages) -}} + {{- $retaggedImages = append $retaggedImages $fullImageName -}} + {{- end -}} +{{- end -}} + +{{- if and (or (gt (len $relocatedImages) 0) (gt (len $replacedImages) 0)) (((.context.Values.global).security).allowInsecureImages) -}} + {{- print "\n\nâš  SECURITY WARNING: Verifying original container images was skipped. Please note this Helm chart was designed, tested, and validated on multiple platforms using a specific set of Bitnami and Tanzu Application Catalog containers. Substituting other containers is likely to cause degraded security and performance, broken chart features, and missing environment variables.\n" -}} +{{- else if (or (gt (len $relocatedImages) 0) (gt (len $replacedImages) 0)) -}} + {{- $errorString := "Original containers have been substituted for unrecognized ones. Deploying this chart with non-standard containers is likely to cause degraded security and performance, broken chart features, and missing environment variables." -}} + {{- $errorString = print $errorString "\n\nUnrecognized images:" -}} + {{- range (concat $relocatedImages $replacedImages) -}} + {{- $errorString = print $errorString "\n - " . -}} + {{- end -}} + {{- if or (contains "docker.io/bitnami/" $originalImages) (contains "docker.io/bitnamiprem/" $originalImages) -}} + {{- $errorString = print "\n\nâš  ERROR: " $errorString -}} + {{- $errorString = print $errorString "\n\nIf you are sure you want to proceed with non-standard containers, you can skip container image verification by setting the global parameter 'global.security.allowInsecureImages' to true." -}} + {{- $errorString = print $errorString "\nFurther information can be obtained at https://github.com/bitnami/charts/issues/30850" -}} + {{- print $errorString | fail -}} + {{- else if gt (len $replacedImages) 0 -}} + {{- $errorString = print "\n\nâš  WARNING: " $errorString -}} + {{- print $errorString -}} + {{- end -}} +{{- else if gt (len $retaggedImages) 0 -}} + {{- $warnString := "\n\nâš  WARNING: Original containers have been retagged. Please note this Helm chart was tested, and validated on multiple platforms using a specific set of Tanzu Application Catalog containers. Substituting original image tags could cause unexpected behavior." -}} + {{- $warnString = print $warnString "\n\nRetagged images:" -}} + {{- range $retaggedImages -}} + {{- $warnString = print $warnString "\n - " . -}} + {{- end -}} + {{- print $warnString -}} +{{- end -}} +{{- end -}} \ No newline at end of file diff --git a/charts/open-webui/charts/redis/charts/common/templates/_images.tpl b/charts/open-webui/charts/redis/charts/common/templates/_images.tpl new file mode 100644 index 0000000..76bb7ce --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/templates/_images.tpl @@ -0,0 +1,115 @@ +{{/* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* vim: set filetype=mustache: */}} +{{/* +Return the proper image name. +If image tag and digest are not defined, termination fallbacks to chart appVersion. +{{ include "common.images.image" ( dict "imageRoot" .Values.path.to.the.image "global" .Values.global "chart" .Chart ) }} +*/}} +{{- define "common.images.image" -}} +{{- $registryName := default .imageRoot.registry ((.global).imageRegistry) -}} +{{- $repositoryName := .imageRoot.repository -}} +{{- $separator := ":" -}} +{{- $termination := .imageRoot.tag | toString -}} + +{{- if not .imageRoot.tag }} + {{- if .chart }} + {{- $termination = .chart.AppVersion | toString -}} + {{- end -}} +{{- end -}} +{{- if .imageRoot.digest }} + {{- $separator = "@" -}} + {{- $termination = .imageRoot.digest | toString -}} +{{- end -}} +{{- if $registryName }} + {{- printf "%s/%s%s%s" $registryName $repositoryName $separator $termination -}} +{{- else -}} + {{- printf "%s%s%s" $repositoryName $separator $termination -}} +{{- end -}} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names (deprecated: use common.images.renderPullSecrets instead) +{{ include "common.images.pullSecrets" ( dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "global" .Values.global) }} +*/}} +{{- define "common.images.pullSecrets" -}} + {{- $pullSecrets := list }} + + {{- range ((.global).imagePullSecrets) -}} + {{- if kindIs "map" . -}} + {{- $pullSecrets = append $pullSecrets .name -}} + {{- else -}} + {{- $pullSecrets = append $pullSecrets . -}} + {{- end }} + {{- end -}} + + {{- range .images -}} + {{- range .pullSecrets -}} + {{- if kindIs "map" . -}} + {{- $pullSecrets = append $pullSecrets .name -}} + {{- else -}} + {{- $pullSecrets = append $pullSecrets . -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- if (not (empty $pullSecrets)) -}} +imagePullSecrets: + {{- range $pullSecrets | uniq }} + - name: {{ . }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names evaluating values as templates +{{ include "common.images.renderPullSecrets" ( dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "context" $) }} +*/}} +{{- define "common.images.renderPullSecrets" -}} + {{- $pullSecrets := list }} + {{- $context := .context }} + + {{- range (($context.Values.global).imagePullSecrets) -}} + {{- if kindIs "map" . -}} + {{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" .name "context" $context)) -}} + {{- else -}} + {{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" $context)) -}} + {{- end -}} + {{- end -}} + + {{- range .images -}} + {{- range .pullSecrets -}} + {{- if kindIs "map" . -}} + {{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" .name "context" $context)) -}} + {{- else -}} + {{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" $context)) -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- if (not (empty $pullSecrets)) -}} +imagePullSecrets: + {{- range $pullSecrets | uniq }} + - name: {{ . }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Return the proper image version (ingores image revision/prerelease info & fallbacks to chart appVersion) +{{ include "common.images.version" ( dict "imageRoot" .Values.path.to.the.image "chart" .Chart ) }} +*/}} +{{- define "common.images.version" -}} +{{- $imageTag := .imageRoot.tag | toString -}} +{{/* regexp from https://github.com/Masterminds/semver/blob/23f51de38a0866c5ef0bfc42b3f735c73107b700/version.go#L41-L44 */}} +{{- if regexMatch `^([0-9]+)(\.[0-9]+)?(\.[0-9]+)?(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?$` $imageTag -}} + {{- $version := semver $imageTag -}} + {{- printf "%d.%d.%d" $version.Major $version.Minor $version.Patch -}} +{{- else -}} + {{- print .chart.AppVersion -}} +{{- end -}} +{{- end -}} + diff --git a/charts/open-webui/charts/redis/charts/common/templates/_ingress.tpl b/charts/open-webui/charts/redis/charts/common/templates/_ingress.tpl new file mode 100644 index 0000000..7d2b879 --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/templates/_ingress.tpl @@ -0,0 +1,73 @@ +{{/* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* vim: set filetype=mustache: */}} + +{{/* +Generate backend entry that is compatible with all Kubernetes API versions. + +Usage: +{{ include "common.ingress.backend" (dict "serviceName" "backendName" "servicePort" "backendPort" "context" $) }} + +Params: + - serviceName - String. Name of an existing service backend + - servicePort - String/Int. Port name (or number) of the service. It will be translated to different yaml depending if it is a string or an integer. + - context - Dict - Required. The context for the template evaluation. +*/}} +{{- define "common.ingress.backend" -}} +{{- $apiVersion := (include "common.capabilities.ingress.apiVersion" .context) -}} +{{- if or (eq $apiVersion "extensions/v1beta1") (eq $apiVersion "networking.k8s.io/v1beta1") -}} +serviceName: {{ .serviceName }} +servicePort: {{ .servicePort }} +{{- else -}} +service: + name: {{ .serviceName }} + port: + {{- if typeIs "string" .servicePort }} + name: {{ .servicePort }} + {{- else if or (typeIs "int" .servicePort) (typeIs "float64" .servicePort) }} + number: {{ .servicePort | int }} + {{- end }} +{{- end -}} +{{- end -}} + +{{/* +Print "true" if the API pathType field is supported +Usage: +{{ include "common.ingress.supportsPathType" . }} +*/}} +{{- define "common.ingress.supportsPathType" -}} +{{- if (semverCompare "<1.18-0" (include "common.capabilities.kubeVersion" .)) -}} +{{- print "false" -}} +{{- else -}} +{{- print "true" -}} +{{- end -}} +{{- end -}} + +{{/* +Returns true if the ingressClassname field is supported +Usage: +{{ include "common.ingress.supportsIngressClassname" . }} +*/}} +{{- define "common.ingress.supportsIngressClassname" -}} +{{- if semverCompare "<1.18-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "false" -}} +{{- else -}} +{{- print "true" -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if cert-manager required annotations for TLS signed +certificates are set in the Ingress annotations +Ref: https://cert-manager.io/docs/usage/ingress/#supported-annotations +Usage: +{{ include "common.ingress.certManagerRequest" ( dict "annotations" .Values.path.to.the.ingress.annotations ) }} +*/}} +{{- define "common.ingress.certManagerRequest" -}} +{{ if or (hasKey .annotations "cert-manager.io/cluster-issuer") (hasKey .annotations "cert-manager.io/issuer") (hasKey .annotations "kubernetes.io/tls-acme") }} + {{- true -}} +{{- end -}} +{{- end -}} diff --git a/charts/open-webui/charts/redis/charts/common/templates/_labels.tpl b/charts/open-webui/charts/redis/charts/common/templates/_labels.tpl new file mode 100644 index 0000000..0a0cc54 --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/templates/_labels.tpl @@ -0,0 +1,46 @@ +{{/* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* vim: set filetype=mustache: */}} + +{{/* +Kubernetes standard labels +{{ include "common.labels.standard" (dict "customLabels" .Values.commonLabels "context" $) -}} +*/}} +{{- define "common.labels.standard" -}} +{{- if and (hasKey . "customLabels") (hasKey . "context") -}} +{{- $default := dict "app.kubernetes.io/name" (include "common.names.name" .context) "helm.sh/chart" (include "common.names.chart" .context) "app.kubernetes.io/instance" .context.Release.Name "app.kubernetes.io/managed-by" .context.Release.Service -}} +{{- with .context.Chart.AppVersion -}} +{{- $_ := set $default "app.kubernetes.io/version" . -}} +{{- end -}} +{{ template "common.tplvalues.merge" (dict "values" (list .customLabels $default) "context" .context) }} +{{- else -}} +app.kubernetes.io/name: {{ include "common.names.name" . }} +helm.sh/chart: {{ include "common.names.chart" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- with .Chart.AppVersion }} +app.kubernetes.io/version: {{ . | quote }} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Labels used on immutable fields such as deploy.spec.selector.matchLabels or svc.spec.selector +{{ include "common.labels.matchLabels" (dict "customLabels" .Values.podLabels "context" $) -}} + +We don't want to loop over custom labels appending them to the selector +since it's very likely that it will break deployments, services, etc. +However, it's important to overwrite the standard labels if the user +overwrote them on metadata.labels fields. +*/}} +{{- define "common.labels.matchLabels" -}} +{{- if and (hasKey . "customLabels") (hasKey . "context") -}} +{{ merge (pick (include "common.tplvalues.render" (dict "value" .customLabels "context" .context) | fromYaml) "app.kubernetes.io/name" "app.kubernetes.io/instance") (dict "app.kubernetes.io/name" (include "common.names.name" .context) "app.kubernetes.io/instance" .context.Release.Name ) | toYaml }} +{{- else -}} +app.kubernetes.io/name: {{ include "common.names.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} +{{- end -}} diff --git a/charts/open-webui/charts/redis/charts/common/templates/_names.tpl b/charts/open-webui/charts/redis/charts/common/templates/_names.tpl new file mode 100644 index 0000000..ba83956 --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/templates/_names.tpl @@ -0,0 +1,71 @@ +{{/* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "common.names.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "common.names.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | 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 "common.names.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 a default fully qualified dependency 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. +Usage: +{{ include "common.names.dependency.fullname" (dict "chartName" "dependency-chart-name" "chartValues" .Values.dependency-chart "context" $) }} +*/}} +{{- define "common.names.dependency.fullname" -}} +{{- if .chartValues.fullnameOverride -}} +{{- .chartValues.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .chartName .chartValues.nameOverride -}} +{{- if contains $name .context.Release.Name -}} +{{- .context.Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .context.Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Allow the release namespace to be overridden for multi-namespace deployments in combined charts. +*/}} +{{- define "common.names.namespace" -}} +{{- default .Release.Namespace .Values.namespaceOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a fully qualified app name adding the installation's namespace. +*/}} +{{- define "common.names.fullname.namespace" -}} +{{- printf "%s-%s" (include "common.names.fullname" .) (include "common.names.namespace" .) | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/charts/open-webui/charts/redis/charts/common/templates/_resources.tpl b/charts/open-webui/charts/redis/charts/common/templates/_resources.tpl new file mode 100644 index 0000000..d8a43e1 --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/templates/_resources.tpl @@ -0,0 +1,50 @@ +{{/* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* vim: set filetype=mustache: */}} + +{{/* +Return a resource request/limit object based on a given preset. +These presets are for basic testing and not meant to be used in production +{{ include "common.resources.preset" (dict "type" "nano") -}} +*/}} +{{- define "common.resources.preset" -}} +{{/* The limits are the requests increased by 50% (except ephemeral-storage and xlarge/2xlarge sizes)*/}} +{{- $presets := dict + "nano" (dict + "requests" (dict "cpu" "100m" "memory" "128Mi" "ephemeral-storage" "50Mi") + "limits" (dict "cpu" "150m" "memory" "192Mi" "ephemeral-storage" "2Gi") + ) + "micro" (dict + "requests" (dict "cpu" "250m" "memory" "256Mi" "ephemeral-storage" "50Mi") + "limits" (dict "cpu" "375m" "memory" "384Mi" "ephemeral-storage" "2Gi") + ) + "small" (dict + "requests" (dict "cpu" "500m" "memory" "512Mi" "ephemeral-storage" "50Mi") + "limits" (dict "cpu" "750m" "memory" "768Mi" "ephemeral-storage" "2Gi") + ) + "medium" (dict + "requests" (dict "cpu" "500m" "memory" "1024Mi" "ephemeral-storage" "50Mi") + "limits" (dict "cpu" "750m" "memory" "1536Mi" "ephemeral-storage" "2Gi") + ) + "large" (dict + "requests" (dict "cpu" "1.0" "memory" "2048Mi" "ephemeral-storage" "50Mi") + "limits" (dict "cpu" "1.5" "memory" "3072Mi" "ephemeral-storage" "2Gi") + ) + "xlarge" (dict + "requests" (dict "cpu" "1.0" "memory" "3072Mi" "ephemeral-storage" "50Mi") + "limits" (dict "cpu" "3.0" "memory" "6144Mi" "ephemeral-storage" "2Gi") + ) + "2xlarge" (dict + "requests" (dict "cpu" "1.0" "memory" "3072Mi" "ephemeral-storage" "50Mi") + "limits" (dict "cpu" "6.0" "memory" "12288Mi" "ephemeral-storage" "2Gi") + ) + }} +{{- if hasKey $presets .type -}} +{{- index $presets .type | toYaml -}} +{{- else -}} +{{- printf "ERROR: Preset key '%s' invalid. Allowed values are %s" .type (join "," (keys $presets)) | fail -}} +{{- end -}} +{{- end -}} diff --git a/charts/open-webui/charts/redis/charts/common/templates/_secrets.tpl b/charts/open-webui/charts/redis/charts/common/templates/_secrets.tpl new file mode 100644 index 0000000..bfef469 --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/templates/_secrets.tpl @@ -0,0 +1,192 @@ +{{/* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* vim: set filetype=mustache: */}} +{{/* +Generate secret name. + +Usage: +{{ include "common.secrets.name" (dict "existingSecret" .Values.path.to.the.existingSecret "defaultNameSuffix" "mySuffix" "context" $) }} + +Params: + - existingSecret - ExistingSecret/String - Optional. The path to the existing secrets in the values.yaml given by the user + to be used instead of the default one. Allows for it to be of type String (just the secret name) for backwards compatibility. + +info: https://github.com/bitnami/charts/tree/main/bitnami/common#existingsecret + - defaultNameSuffix - String - Optional. It is used only if we have several secrets in the same deployment. + - context - Dict - Required. The context for the template evaluation. +*/}} +{{- define "common.secrets.name" -}} +{{- $name := (include "common.names.fullname" .context) -}} + +{{- if .defaultNameSuffix -}} +{{- $name = printf "%s-%s" $name .defaultNameSuffix | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- with .existingSecret -}} +{{- if not (typeIs "string" .) -}} +{{- with .name -}} +{{- $name = . -}} +{{- end -}} +{{- else -}} +{{- $name = . -}} +{{- end -}} +{{- end -}} + +{{- printf "%s" $name -}} +{{- end -}} + +{{/* +Generate secret key. + +Usage: +{{ include "common.secrets.key" (dict "existingSecret" .Values.path.to.the.existingSecret "key" "keyName") }} + +Params: + - existingSecret - ExistingSecret/String - Optional. The path to the existing secrets in the values.yaml given by the user + to be used instead of the default one. Allows for it to be of type String (just the secret name) for backwards compatibility. + +info: https://github.com/bitnami/charts/tree/main/bitnami/common#existingsecret + - key - String - Required. Name of the key in the secret. +*/}} +{{- define "common.secrets.key" -}} +{{- $key := .key -}} + +{{- if .existingSecret -}} + {{- if not (typeIs "string" .existingSecret) -}} + {{- if .existingSecret.keyMapping -}} + {{- $key = index .existingSecret.keyMapping $.key -}} + {{- end -}} + {{- end }} +{{- end -}} + +{{- printf "%s" $key -}} +{{- end -}} + +{{/* +Generate secret password or retrieve one if already created. + +Usage: +{{ include "common.secrets.passwords.manage" (dict "secret" "secret-name" "key" "keyName" "providedValues" (list "path.to.password1" "path.to.password2") "length" 10 "strong" false "chartName" "chartName" "honorProvidedValues" false "context" $) }} + +Params: + - secret - String - Required - Name of the 'Secret' resource where the password is stored. + - key - String - Required - Name of the key in the secret. + - providedValues - List - Required - The path to the validating value in the values.yaml, e.g: "mysql.password". Will pick first parameter with a defined value. + - length - int - Optional - Length of the generated random password. + - strong - Boolean - Optional - Whether to add symbols to the generated random password. + - chartName - String - Optional - Name of the chart used when said chart is deployed as a subchart. + - context - Context - Required - Parent context. + - failOnNew - Boolean - Optional - Default to true. If set to false, skip errors adding new keys to existing secrets. + - skipB64enc - Boolean - Optional - Default to false. If set to true, no the secret will not be base64 encrypted. + - skipQuote - Boolean - Optional - Default to false. If set to true, no quotes will be added around the secret. + - honorProvidedValues - Boolean - Optional - Default to false. If set to true, the values in providedValues have higher priority than an existing secret +The order in which this function returns a secret password: + 1. Password provided via the values.yaml if honorProvidedValues = true + (If one of the keys passed to the 'providedValues' parameter to this function is a valid path to a key in the values.yaml and has a value, the value of the first key with a value will be returned) + 2. Already existing 'Secret' resource + (If a 'Secret' resource is found under the name provided to the 'secret' parameter to this function and that 'Secret' resource contains a key with the name passed as the 'key' parameter to this function then the value of this existing secret password will be returned) + 3. Password provided via the values.yaml if honorProvidedValues = false + (If one of the keys passed to the 'providedValues' parameter to this function is a valid path to a key in the values.yaml and has a value, the value of the first key with a value will be returned) + 4. Randomly generated secret password + (A new random secret password with the length specified in the 'length' parameter will be generated and returned) + +*/}} +{{- define "common.secrets.passwords.manage" -}} + +{{- $password := "" }} +{{- $subchart := "" }} +{{- $chartName := default "" .chartName }} +{{- $passwordLength := default 10 .length }} +{{- $providedPasswordKey := include "common.utils.getKeyFromList" (dict "keys" .providedValues "context" $.context) }} +{{- $providedPasswordValue := include "common.utils.getValueFromKey" (dict "key" $providedPasswordKey "context" $.context) }} +{{- $secretData := (lookup "v1" "Secret" (include "common.names.namespace" .context) .secret).data }} +{{- if $secretData }} + {{- if hasKey $secretData .key }} + {{- $password = index $secretData .key | b64dec }} + {{- else if not (eq .failOnNew false) }} + {{- printf "\nPASSWORDS ERROR: The secret \"%s\" does not contain the key \"%s\"\n" .secret .key | fail -}} + {{- end -}} +{{- end }} + +{{- if and $providedPasswordValue .honorProvidedValues }} + {{- $password = $providedPasswordValue | toString }} +{{- end }} + +{{- if not $password }} + {{- if $providedPasswordValue }} + {{- $password = $providedPasswordValue | toString }} + {{- else }} + {{- if .context.Values.enabled }} + {{- $subchart = $chartName }} + {{- end -}} + + {{- if not (eq .failOnNew false) }} + {{- $requiredPassword := dict "valueKey" $providedPasswordKey "secret" .secret "field" .key "subchart" $subchart "context" $.context -}} + {{- $requiredPasswordError := include "common.validations.values.single.empty" $requiredPassword -}} + {{- $passwordValidationErrors := list $requiredPasswordError -}} + {{- include "common.errors.upgrade.passwords.empty" (dict "validationErrors" $passwordValidationErrors "context" $.context) -}} + {{- end }} + + {{- if .strong }} + {{- $subStr := list (lower (randAlpha 1)) (randNumeric 1) (upper (randAlpha 1)) | join "_" }} + {{- $password = randAscii $passwordLength }} + {{- $password = regexReplaceAllLiteral "\\W" $password "@" | substr 5 $passwordLength }} + {{- $password = printf "%s%s" $subStr $password | toString | shuffle }} + {{- else }} + {{- $password = randAlphaNum $passwordLength }} + {{- end }} + {{- end -}} +{{- end -}} +{{- if not .skipB64enc }} +{{- $password = $password | b64enc }} +{{- end -}} +{{- if .skipQuote -}} +{{- printf "%s" $password -}} +{{- else -}} +{{- printf "%s" $password | quote -}} +{{- end -}} +{{- end -}} + +{{/* +Reuses the value from an existing secret, otherwise sets its value to a default value. + +Usage: +{{ include "common.secrets.lookup" (dict "secret" "secret-name" "key" "keyName" "defaultValue" .Values.myValue "context" $) }} + +Params: + - secret - String - Required - Name of the 'Secret' resource where the password is stored. + - key - String - Required - Name of the key in the secret. + - defaultValue - String - Required - The path to the validating value in the values.yaml, e.g: "mysql.password". Will pick first parameter with a defined value. + - context - Context - Required - Parent context. + +*/}} +{{- define "common.secrets.lookup" -}} +{{- $value := "" -}} +{{- $secretData := (lookup "v1" "Secret" (include "common.names.namespace" .context) .secret).data -}} +{{- if and $secretData (hasKey $secretData .key) -}} + {{- $value = index $secretData .key -}} +{{- else if .defaultValue -}} + {{- $value = .defaultValue | toString | b64enc -}} +{{- end -}} +{{- if $value -}} +{{- printf "%s" $value -}} +{{- end -}} +{{- end -}} + +{{/* +Returns whether a previous generated secret already exists + +Usage: +{{ include "common.secrets.exists" (dict "secret" "secret-name" "context" $) }} + +Params: + - secret - String - Required - Name of the 'Secret' resource where the password is stored. + - context - Context - Required - Parent context. +*/}} +{{- define "common.secrets.exists" -}} +{{- $secret := (lookup "v1" "Secret" (include "common.names.namespace" .context) .secret) }} +{{- if $secret }} + {{- true -}} +{{- end -}} +{{- end -}} diff --git a/charts/open-webui/charts/redis/charts/common/templates/_storage.tpl b/charts/open-webui/charts/redis/charts/common/templates/_storage.tpl new file mode 100644 index 0000000..aa75856 --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/templates/_storage.tpl @@ -0,0 +1,21 @@ +{{/* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* vim: set filetype=mustache: */}} + +{{/* +Return the proper Storage Class +{{ include "common.storage.class" ( dict "persistence" .Values.path.to.the.persistence "global" $) }} +*/}} +{{- define "common.storage.class" -}} +{{- $storageClass := (.global).storageClass | default .persistence.storageClass | default (.global).defaultStorageClass | default "" -}} +{{- if $storageClass -}} + {{- if (eq "-" $storageClass) -}} + {{- printf "storageClassName: \"\"" -}} + {{- else -}} + {{- printf "storageClassName: %s" $storageClass -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/open-webui/charts/redis/charts/common/templates/_tplvalues.tpl b/charts/open-webui/charts/redis/charts/common/templates/_tplvalues.tpl new file mode 100644 index 0000000..a04f4c1 --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/templates/_tplvalues.tpl @@ -0,0 +1,52 @@ +{{/* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* vim: set filetype=mustache: */}} +{{/* +Renders a value that contains template perhaps with scope if the scope is present. +Usage: +{{ include "common.tplvalues.render" ( dict "value" .Values.path.to.the.Value "context" $ ) }} +{{ include "common.tplvalues.render" ( dict "value" .Values.path.to.the.Value "context" $ "scope" $app ) }} +*/}} +{{- define "common.tplvalues.render" -}} +{{- $value := typeIs "string" .value | ternary .value (.value | toYaml) }} +{{- if contains "{{" (toJson .value) }} + {{- if .scope }} + {{- tpl (cat "{{- with $.RelativeScope -}}" $value "{{- end }}") (merge (dict "RelativeScope" .scope) .context) }} + {{- else }} + {{- tpl $value .context }} + {{- end }} +{{- else }} + {{- $value }} +{{- end }} +{{- end -}} + +{{/* +Merge a list of values that contains template after rendering them. +Merge precedence is consistent with http://masterminds.github.io/sprig/dicts.html#merge-mustmerge +Usage: +{{ include "common.tplvalues.merge" ( dict "values" (list .Values.path.to.the.Value1 .Values.path.to.the.Value2) "context" $ ) }} +*/}} +{{- define "common.tplvalues.merge" -}} +{{- $dst := dict -}} +{{- range .values -}} +{{- $dst = include "common.tplvalues.render" (dict "value" . "context" $.context "scope" $.scope) | fromYaml | merge $dst -}} +{{- end -}} +{{ $dst | toYaml }} +{{- end -}} + +{{/* +Merge a list of values that contains template after rendering them. +Merge precedence is consistent with https://masterminds.github.io/sprig/dicts.html#mergeoverwrite-mustmergeoverwrite +Usage: +{{ include "common.tplvalues.merge-overwrite" ( dict "values" (list .Values.path.to.the.Value1 .Values.path.to.the.Value2) "context" $ ) }} +*/}} +{{- define "common.tplvalues.merge-overwrite" -}} +{{- $dst := dict -}} +{{- range .values -}} +{{- $dst = include "common.tplvalues.render" (dict "value" . "context" $.context "scope" $.scope) | fromYaml | mergeOverwrite $dst -}} +{{- end -}} +{{ $dst | toYaml }} +{{- end -}} diff --git a/charts/open-webui/charts/redis/charts/common/templates/_utils.tpl b/charts/open-webui/charts/redis/charts/common/templates/_utils.tpl new file mode 100644 index 0000000..d53c74a --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/templates/_utils.tpl @@ -0,0 +1,77 @@ +{{/* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* vim: set filetype=mustache: */}} +{{/* +Print instructions to get a secret value. +Usage: +{{ include "common.utils.secret.getvalue" (dict "secret" "secret-name" "field" "secret-value-field" "context" $) }} +*/}} +{{- define "common.utils.secret.getvalue" -}} +{{- $varname := include "common.utils.fieldToEnvVar" . -}} +export {{ $varname }}=$(kubectl get secret --namespace {{ include "common.names.namespace" .context | quote }} {{ .secret }} -o jsonpath="{.data.{{ .field }}}" | base64 -d) +{{- end -}} + +{{/* +Build env var name given a field +Usage: +{{ include "common.utils.fieldToEnvVar" dict "field" "my-password" }} +*/}} +{{- define "common.utils.fieldToEnvVar" -}} + {{- $fieldNameSplit := splitList "-" .field -}} + {{- $upperCaseFieldNameSplit := list -}} + + {{- range $fieldNameSplit -}} + {{- $upperCaseFieldNameSplit = append $upperCaseFieldNameSplit ( upper . ) -}} + {{- end -}} + + {{ join "_" $upperCaseFieldNameSplit }} +{{- end -}} + +{{/* +Gets a value from .Values given +Usage: +{{ include "common.utils.getValueFromKey" (dict "key" "path.to.key" "context" $) }} +*/}} +{{- define "common.utils.getValueFromKey" -}} +{{- $splitKey := splitList "." .key -}} +{{- $value := "" -}} +{{- $latestObj := $.context.Values -}} +{{- range $splitKey -}} + {{- if not $latestObj -}} + {{- printf "please review the entire path of '%s' exists in values" $.key | fail -}} + {{- end -}} + {{- $value = ( index $latestObj . ) -}} + {{- $latestObj = $value -}} +{{- end -}} +{{- printf "%v" (default "" $value) -}} +{{- end -}} + +{{/* +Returns first .Values key with a defined value or first of the list if all non-defined +Usage: +{{ include "common.utils.getKeyFromList" (dict "keys" (list "path.to.key1" "path.to.key2") "context" $) }} +*/}} +{{- define "common.utils.getKeyFromList" -}} +{{- $key := first .keys -}} +{{- $reverseKeys := reverse .keys }} +{{- range $reverseKeys }} + {{- $value := include "common.utils.getValueFromKey" (dict "key" . "context" $.context ) }} + {{- if $value -}} + {{- $key = . }} + {{- end -}} +{{- end -}} +{{- printf "%s" $key -}} +{{- end -}} + +{{/* +Checksum a template at "path" containing a *single* resource (ConfigMap,Secret) for use in pod annotations, excluding the metadata (see #18376). +Usage: +{{ include "common.utils.checksumTemplate" (dict "path" "/configmap.yaml" "context" $) }} +*/}} +{{- define "common.utils.checksumTemplate" -}} +{{- $obj := include (print .context.Template.BasePath .path) .context | fromYaml -}} +{{ omit $obj "apiVersion" "kind" "metadata" | toYaml | sha256sum }} +{{- end -}} diff --git a/charts/open-webui/charts/redis/charts/common/templates/_warnings.tpl b/charts/open-webui/charts/redis/charts/common/templates/_warnings.tpl new file mode 100644 index 0000000..62c44df --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/templates/_warnings.tpl @@ -0,0 +1,109 @@ +{{/* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* vim: set filetype=mustache: */}} +{{/* +Warning about using rolling tag. +Usage: +{{ include "common.warnings.rollingTag" .Values.path.to.the.imageRoot }} +*/}} +{{- define "common.warnings.rollingTag" -}} + +{{- if and (contains "bitnami/" .repository) (not (.tag | toString | regexFind "-r\\d+$|sha256:")) }} +WARNING: Rolling tag detected ({{ .repository }}:{{ .tag }}), please note that it is strongly recommended to avoid using rolling tags in a production environment. ++info https://techdocs.broadcom.com/us/en/vmware-tanzu/application-catalog/tanzu-application-catalog/services/tac-doc/apps-tutorials-understand-rolling-tags-containers-index.html +{{- end }} +{{- end -}} + +{{/* +Warning about replaced images from the original. +Usage: +{{ include "common.warnings.modifiedImages" (dict "images" (list .Values.path.to.the.imageRoot) "context" $) }} +*/}} +{{- define "common.warnings.modifiedImages" -}} +{{- $affectedImages := list -}} +{{- $printMessage := false -}} +{{- $originalImages := .context.Chart.Annotations.images -}} +{{- range .images -}} + {{- $fullImageName := printf (printf "%s/%s:%s" .registry .repository .tag) -}} + {{- if not (contains $fullImageName $originalImages) }} + {{- $affectedImages = append $affectedImages (printf "%s/%s:%s" .registry .repository .tag) -}} + {{- $printMessage = true -}} + {{- end -}} +{{- end -}} +{{- if $printMessage }} + +âš  SECURITY WARNING: Original containers have been substituted. This Helm chart was designed, tested, and validated on multiple platforms using a specific set of Bitnami and Tanzu Application Catalog containers. Substituting other containers is likely to cause degraded security and performance, broken chart features, and missing environment variables. + +Substituted images detected: +{{- range $affectedImages }} + - {{ . }} +{{- end }} +{{- end -}} +{{- end -}} + +{{/* +Warning about not setting the resource object in all deployments. +Usage: +{{ include "common.warnings.resources" (dict "sections" (list "path1" "path2") context $) }} +Example: +{{- include "common.warnings.resources" (dict "sections" (list "csiProvider.provider" "server" "volumePermissions" "") "context" $) }} +The list in the example assumes that the following values exist: + - csiProvider.provider.resources + - server.resources + - volumePermissions.resources + - resources +*/}} +{{- define "common.warnings.resources" -}} +{{- $values := .context.Values -}} +{{- $printMessage := false -}} +{{ $affectedSections := list -}} +{{- range .sections -}} + {{- if eq . "" -}} + {{/* Case where the resources section is at the root (one main deployment in the chart) */}} + {{- if not (index $values "resources") -}} + {{- $affectedSections = append $affectedSections "resources" -}} + {{- $printMessage = true -}} + {{- end -}} + {{- else -}} + {{/* Case where the are multiple resources sections (more than one main deployment in the chart) */}} + {{- $keys := split "." . -}} + {{/* We iterate through the different levels until arriving to the resource section. Example: a.b.c.resources */}} + {{- $section := $values -}} + {{- range $keys -}} + {{- $section = index $section . -}} + {{- end -}} + {{- if not (index $section "resources") -}} + {{/* If the section has enabled=false or replicaCount=0, do not include it */}} + {{- if and (hasKey $section "enabled") -}} + {{- if index $section "enabled" -}} + {{/* enabled=true */}} + {{- $affectedSections = append $affectedSections (printf "%s.resources" .) -}} + {{- $printMessage = true -}} + {{- end -}} + {{- else if and (hasKey $section "replicaCount") -}} + {{/* We need a casting to int because number 0 is not treated as an int by default */}} + {{- if (gt (index $section "replicaCount" | int) 0) -}} + {{/* replicaCount > 0 */}} + {{- $affectedSections = append $affectedSections (printf "%s.resources" .) -}} + {{- $printMessage = true -}} + {{- end -}} + {{- else -}} + {{/* Default case, add it to the affected sections */}} + {{- $affectedSections = append $affectedSections (printf "%s.resources" .) -}} + {{- $printMessage = true -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} +{{- if $printMessage }} + +WARNING: There are "resources" sections in the chart not set. Using "resourcesPreset" is not recommended for production. For production installations, please set the following values according to your workload needs: +{{- range $affectedSections }} + - {{ . }} +{{- end }} ++info https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ +{{- end -}} +{{- end -}} diff --git a/charts/open-webui/charts/redis/charts/common/templates/validations/_cassandra.tpl b/charts/open-webui/charts/redis/charts/common/templates/validations/_cassandra.tpl new file mode 100644 index 0000000..f8fd213 --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/templates/validations/_cassandra.tpl @@ -0,0 +1,51 @@ +{{/* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* vim: set filetype=mustache: */}} +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.cassandra.values.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether Cassandra is used as subchart or not. Default: false +*/}} +{{- define "common.cassandra.values.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.cassandra.dbUser.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.dbUser.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled cassandra. + +Usage: +{{ include "common.cassandra.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.cassandra.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.cassandra.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key dbUser + +Usage: +{{ include "common.cassandra.values.key.dbUser" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether Cassandra is used as subchart or not. Default: false +*/}} +{{- define "common.cassandra.values.key.dbUser" -}} + {{- if .subchart -}} + cassandra.dbUser + {{- else -}} + dbUser + {{- end -}} +{{- end -}} diff --git a/charts/open-webui/charts/redis/charts/common/templates/validations/_mariadb.tpl b/charts/open-webui/charts/redis/charts/common/templates/validations/_mariadb.tpl new file mode 100644 index 0000000..6ea8c0f --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/templates/validations/_mariadb.tpl @@ -0,0 +1,108 @@ +{{/* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* vim: set filetype=mustache: */}} +{{/* +Validate MariaDB required passwords are not empty. + +Usage: +{{ include "common.validations.values.mariadb.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where MariaDB values are stored, e.g: "mysql-passwords-secret" + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.mariadb.passwords" -}} + {{- $existingSecret := include "common.mariadb.values.auth.existingSecret" . -}} + {{- $enabled := include "common.mariadb.values.enabled" . -}} + {{- $architecture := include "common.mariadb.values.architecture" . -}} + {{- $authPrefix := include "common.mariadb.values.key.auth" . -}} + {{- $valueKeyRootPassword := printf "%s.rootPassword" $authPrefix -}} + {{- $valueKeyUsername := printf "%s.username" $authPrefix -}} + {{- $valueKeyPassword := printf "%s.password" $authPrefix -}} + {{- $valueKeyReplicationPassword := printf "%s.replicationPassword" $authPrefix -}} + + {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $requiredRootPassword := dict "valueKey" $valueKeyRootPassword "secret" .secret "field" "mariadb-root-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredRootPassword -}} + + {{- $valueUsername := include "common.utils.getValueFromKey" (dict "key" $valueKeyUsername "context" .context) }} + {{- if not (empty $valueUsername) -}} + {{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "mariadb-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPassword -}} + {{- end -}} + + {{- if (eq $architecture "replication") -}} + {{- $requiredReplicationPassword := dict "valueKey" $valueKeyReplicationPassword "secret" .secret "field" "mariadb-replication-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredReplicationPassword -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.mariadb.values.auth.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.mariadb.values.auth.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.mariadb.auth.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.auth.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled mariadb. + +Usage: +{{ include "common.mariadb.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.mariadb.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.mariadb.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for architecture + +Usage: +{{ include "common.mariadb.values.architecture" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.mariadb.values.architecture" -}} + {{- if .subchart -}} + {{- .context.Values.mariadb.architecture -}} + {{- else -}} + {{- .context.Values.architecture -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key auth + +Usage: +{{ include "common.mariadb.values.key.auth" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.mariadb.values.key.auth" -}} + {{- if .subchart -}} + mariadb.auth + {{- else -}} + auth + {{- end -}} +{{- end -}} diff --git a/charts/open-webui/charts/redis/charts/common/templates/validations/_mongodb.tpl b/charts/open-webui/charts/redis/charts/common/templates/validations/_mongodb.tpl new file mode 100644 index 0000000..e678a6d --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/templates/validations/_mongodb.tpl @@ -0,0 +1,67 @@ +{{/* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* vim: set filetype=mustache: */}} +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.mongodb.values.auth.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MongoDb is used as subchart or not. Default: false +*/}} +{{- define "common.mongodb.values.auth.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.mongodb.auth.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.auth.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled mongodb. + +Usage: +{{ include "common.mongodb.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.mongodb.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.mongodb.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key auth + +Usage: +{{ include "common.mongodb.values.key.auth" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MongoDB® is used as subchart or not. Default: false +*/}} +{{- define "common.mongodb.values.key.auth" -}} + {{- if .subchart -}} + mongodb.auth + {{- else -}} + auth + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for architecture + +Usage: +{{ include "common.mongodb.values.architecture" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MongoDB® is used as subchart or not. Default: false +*/}} +{{- define "common.mongodb.values.architecture" -}} + {{- if .subchart -}} + {{- .context.Values.mongodb.architecture -}} + {{- else -}} + {{- .context.Values.architecture -}} + {{- end -}} +{{- end -}} diff --git a/charts/open-webui/charts/redis/charts/common/templates/validations/_mysql.tpl b/charts/open-webui/charts/redis/charts/common/templates/validations/_mysql.tpl new file mode 100644 index 0000000..fbb65c3 --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/templates/validations/_mysql.tpl @@ -0,0 +1,67 @@ +{{/* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* vim: set filetype=mustache: */}} +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.mysql.values.auth.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MySQL is used as subchart or not. Default: false +*/}} +{{- define "common.mysql.values.auth.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.mysql.auth.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.auth.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled mysql. + +Usage: +{{ include "common.mysql.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.mysql.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.mysql.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for architecture + +Usage: +{{ include "common.mysql.values.architecture" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MySQL is used as subchart or not. Default: false +*/}} +{{- define "common.mysql.values.architecture" -}} + {{- if .subchart -}} + {{- .context.Values.mysql.architecture -}} + {{- else -}} + {{- .context.Values.architecture -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key auth + +Usage: +{{ include "common.mysql.values.key.auth" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MySQL is used as subchart or not. Default: false +*/}} +{{- define "common.mysql.values.key.auth" -}} + {{- if .subchart -}} + mysql.auth + {{- else -}} + auth + {{- end -}} +{{- end -}} diff --git a/charts/open-webui/charts/redis/charts/common/templates/validations/_postgresql.tpl b/charts/open-webui/charts/redis/charts/common/templates/validations/_postgresql.tpl new file mode 100644 index 0000000..51d4716 --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/templates/validations/_postgresql.tpl @@ -0,0 +1,105 @@ +{{/* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* vim: set filetype=mustache: */}} +{{/* +Auxiliary function to decide whether evaluate global values. + +Usage: +{{ include "common.postgresql.values.use.global" (dict "key" "key-of-global" "context" $) }} +Params: + - key - String - Required. Field to be evaluated within global, e.g: "existingSecret" +*/}} +{{- define "common.postgresql.values.use.global" -}} + {{- if .context.Values.global -}} + {{- if .context.Values.global.postgresql -}} + {{- index .context.Values.global.postgresql .key | quote -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.postgresql.values.existingSecret" (dict "context" $) }} +*/}} +{{- define "common.postgresql.values.existingSecret" -}} + {{- $globalValue := include "common.postgresql.values.use.global" (dict "key" "existingSecret" "context" .context) -}} + + {{- if .subchart -}} + {{- default (.context.Values.postgresql.existingSecret | quote) $globalValue -}} + {{- else -}} + {{- default (.context.Values.existingSecret | quote) $globalValue -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled postgresql. + +Usage: +{{ include "common.postgresql.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.postgresql.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.postgresql.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key postgressPassword. + +Usage: +{{ include "common.postgresql.values.key.postgressPassword" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false +*/}} +{{- define "common.postgresql.values.key.postgressPassword" -}} + {{- $globalValue := include "common.postgresql.values.use.global" (dict "key" "postgresqlUsername" "context" .context) -}} + + {{- if not $globalValue -}} + {{- if .subchart -}} + postgresql.postgresqlPassword + {{- else -}} + postgresqlPassword + {{- end -}} + {{- else -}} + global.postgresql.postgresqlPassword + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled.replication. + +Usage: +{{ include "common.postgresql.values.enabled.replication" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false +*/}} +{{- define "common.postgresql.values.enabled.replication" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.postgresql.replication.enabled -}} + {{- else -}} + {{- printf "%v" .context.Values.replication.enabled -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key replication.password. + +Usage: +{{ include "common.postgresql.values.key.replicationPassword" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false +*/}} +{{- define "common.postgresql.values.key.replicationPassword" -}} + {{- if .subchart -}} + postgresql.replication.password + {{- else -}} + replication.password + {{- end -}} +{{- end -}} diff --git a/charts/open-webui/charts/redis/charts/common/templates/validations/_redis.tpl b/charts/open-webui/charts/redis/charts/common/templates/validations/_redis.tpl new file mode 100644 index 0000000..9fedfef --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/templates/validations/_redis.tpl @@ -0,0 +1,48 @@ +{{/* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + + +{{/* vim: set filetype=mustache: */}} +{{/* +Auxiliary function to get the right value for enabled redis. + +Usage: +{{ include "common.redis.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.redis.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.redis.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right prefix path for the values + +Usage: +{{ include "common.redis.values.key.prefix" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether redis is used as subchart or not. Default: false +*/}} +{{- define "common.redis.values.keys.prefix" -}} + {{- if .subchart -}}redis.{{- else -}}{{- end -}} +{{- end -}} + +{{/* +Checks whether the redis chart's includes the standarizations (version >= 14) + +Usage: +{{ include "common.redis.values.standarized.version" (dict "context" $) }} +*/}} +{{- define "common.redis.values.standarized.version" -}} + + {{- $standarizedAuth := printf "%s%s" (include "common.redis.values.keys.prefix" .) "auth" -}} + {{- $standarizedAuthValues := include "common.utils.getValueFromKey" (dict "key" $standarizedAuth "context" .context) }} + + {{- if $standarizedAuthValues -}} + {{- true -}} + {{- end -}} +{{- end -}} diff --git a/charts/open-webui/charts/redis/charts/common/templates/validations/_validations.tpl b/charts/open-webui/charts/redis/charts/common/templates/validations/_validations.tpl new file mode 100644 index 0000000..7cdee61 --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/templates/validations/_validations.tpl @@ -0,0 +1,51 @@ +{{/* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* vim: set filetype=mustache: */}} +{{/* +Validate values must not be empty. + +Usage: +{{- $validateValueConf00 := (dict "valueKey" "path.to.value" "secret" "secretName" "field" "password-00") -}} +{{- $validateValueConf01 := (dict "valueKey" "path.to.value" "secret" "secretName" "field" "password-01") -}} +{{ include "common.validations.values.empty" (dict "required" (list $validateValueConf00 $validateValueConf01) "context" $) }} + +Validate value params: + - valueKey - String - Required. The path to the validating value in the values.yaml, e.g: "mysql.password" + - secret - String - Optional. Name of the secret where the validating value is generated/stored, e.g: "mysql-passwords-secret" + - field - String - Optional. Name of the field in the secret data, e.g: "mysql-password" +*/}} +{{- define "common.validations.values.multiple.empty" -}} + {{- range .required -}} + {{- include "common.validations.values.single.empty" (dict "valueKey" .valueKey "secret" .secret "field" .field "context" $.context) -}} + {{- end -}} +{{- end -}} + +{{/* +Validate a value must not be empty. + +Usage: +{{ include "common.validations.value.empty" (dict "valueKey" "mariadb.password" "secret" "secretName" "field" "my-password" "subchart" "subchart" "context" $) }} + +Validate value params: + - valueKey - String - Required. The path to the validating value in the values.yaml, e.g: "mysql.password" + - secret - String - Optional. Name of the secret where the validating value is generated/stored, e.g: "mysql-passwords-secret" + - field - String - Optional. Name of the field in the secret data, e.g: "mysql-password" + - subchart - String - Optional - Name of the subchart that the validated password is part of. +*/}} +{{- define "common.validations.values.single.empty" -}} + {{- $value := include "common.utils.getValueFromKey" (dict "key" .valueKey "context" .context) }} + {{- $subchart := ternary "" (printf "%s." .subchart) (empty .subchart) }} + + {{- if not $value -}} + {{- $varname := "my-value" -}} + {{- $getCurrentValue := "" -}} + {{- if and .secret .field -}} + {{- $varname = include "common.utils.fieldToEnvVar" . -}} + {{- $getCurrentValue = printf " To get the current value:\n\n %s\n" (include "common.utils.secret.getvalue" .) -}} + {{- end -}} + {{- printf "\n '%s' must not be empty, please add '--set %s%s=$%s' to the command.%s" .valueKey $subchart .valueKey $varname $getCurrentValue -}} + {{- end -}} +{{- end -}} diff --git a/charts/open-webui/charts/redis/charts/common/values.yaml b/charts/open-webui/charts/redis/charts/common/values.yaml new file mode 100644 index 0000000..de2cac5 --- /dev/null +++ b/charts/open-webui/charts/redis/charts/common/values.yaml @@ -0,0 +1,8 @@ +# Copyright Broadcom, Inc. All Rights Reserved. +# SPDX-License-Identifier: APACHE-2.0 + +## bitnami/common +## It is required by CI/CD tools and processes. +## @skip exampleValue +## +exampleValue: common-chart diff --git a/charts/open-webui/charts/redis/templates/NOTES.txt b/charts/open-webui/charts/redis/templates/NOTES.txt new file mode 100644 index 0000000..3094ae9 --- /dev/null +++ b/charts/open-webui/charts/redis/templates/NOTES.txt @@ -0,0 +1,216 @@ +CHART NAME: {{ .Chart.Name }} +CHART VERSION: {{ .Chart.Version }} +APP VERSION: {{ .Chart.AppVersion }} + +Did you know there are enterprise versions of the Bitnami catalog? For enhanced secure software supply chain features, unlimited pulls from Docker, LTS support, or application customization, see Bitnami Premium or Tanzu Application Catalog. See https://www.arrow.com/globalecs/na/vendors/bitnami for more information. + +** Please be patient while the chart is being deployed ** + +{{- if .Values.diagnosticMode.enabled }} +The chart has been deployed in diagnostic mode. All probes have been disabled and the command has been overwritten with: + + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 4 }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 4 }} + +Get the list of pods by executing: + + kubectl get pods --namespace {{ include "common.names.namespace" . }} -l app.kubernetes.io/instance={{ .Release.Name }} + +Access the pod you want to debug by executing + + kubectl exec --namespace {{ include "common.names.namespace" . }} -ti -- bash + +In order to replicate the container startup scripts execute this command: + +For Redis: + + /opt/bitnami/scripts/redis/entrypoint.sh /opt/bitnami/scripts/redis/run.sh + +{{- if .Values.sentinel.enabled }} + +For Redis Sentinel: + + /opt/bitnami/scripts/redis-sentinel/entrypoint.sh /opt/bitnami/scripts/redis-sentinel/run.sh + +{{- end }} +{{- else }} + +{{- if contains .Values.master.service.type "LoadBalancer" }} +{{- if not .Values.auth.enabled }} +{{ if and (not .Values.networkPolicy.enabled) (.Values.networkPolicy.allowExternal) }} + +------------------------------------------------------------------------------- + WARNING + + By specifying "master.service.type=LoadBalancer" and "auth.enabled=false" you have + most likely exposed the Redis® service externally without any authentication + mechanism. + + For security reasons, we strongly suggest that you switch to "ClusterIP" or + "NodePort". As alternative, you can also switch to "auth.enabled=true" + providing a valid password on "password" parameter. + +------------------------------------------------------------------------------- +{{- end }} +{{- end }} +{{- end }} + +{{- if and .Values.auth.usePasswordFiles (not .Values.auth.usePasswordFileFromSecret) (or (empty .Values.master.initContainers) (empty .Values.replica.initContainers)) }} + +------------------------------------------------------------------------------- + WARNING + + By specifying ".Values.auth.usePasswordFiles=true" and ".Values.auth.usePasswordFileFromSecret=false" + Redis is expecting that the password is mounted as a file in each pod + (by default in /opt/bitnami/redis/secrets/redis-password) + + Ensure that you specify the respective initContainers in + both .Values.master.initContainers and .Values.replica.initContainers + in order to populate the contents of this file. + +------------------------------------------------------------------------------- +{{- end }} + +{{- if eq .Values.architecture "replication" }} +{{- if .Values.sentinel.enabled }} + +Redis® can be accessed via port {{ .Values.sentinel.service.ports.redis }} on the following DNS name from within your cluster: + + {{ template "common.names.fullname" . }}.{{ include "common.names.namespace" . }}.svc.{{ .Values.clusterDomain }} for read only operations + +For read/write operations, first access the Redis® Sentinel cluster, which is available in port {{ .Values.sentinel.service.ports.sentinel }} using the same domain name above. + +{{- else }} + +Redis® can be accessed on the following DNS names from within your cluster: + + {{ printf "%s-master.%s.svc.%s" (include "common.names.fullname" .) (include "common.names.namespace" . ) .Values.clusterDomain }} for read/write operations (port {{ .Values.master.service.ports.redis }}) + {{ printf "%s-replicas.%s.svc.%s" (include "common.names.fullname" .) (include "common.names.namespace" . ) .Values.clusterDomain }} for read-only operations (port {{ .Values.replica.service.ports.redis }}) + +{{- end }} +{{- else }} + +Redis® can be accessed via port {{ .Values.master.service.ports.redis }} on the following DNS name from within your cluster: + + {{ template "common.names.fullname" . }}-master.{{ include "common.names.namespace" . }}.svc.{{ .Values.clusterDomain }} + +{{- end }} + +{{ if .Values.auth.enabled }} + +To get your password run: + + export REDIS_PASSWORD=$(kubectl get secret --namespace {{ include "common.names.namespace" . }} {{ template "redis.secretName" . }} -o jsonpath="{.data.redis-password}" | base64 -d) + +{{- end }} + +To connect to your Redis® server: + +1. Run a Redis® pod that you can use as a client: + + kubectl run --namespace {{ include "common.names.namespace" . }} redis-client --restart='Never' {{ if .Values.auth.enabled }} --env REDIS_PASSWORD=$REDIS_PASSWORD {{ end }} --image {{ template "redis.image" . }} --command -- sleep infinity + +{{- if .Values.tls.enabled }} + + Copy your TLS certificates to the pod: + + kubectl cp --namespace {{ include "common.names.namespace" . }} /path/to/client.cert redis-client:/tmp/client.cert + kubectl cp --namespace {{ include "common.names.namespace" . }} /path/to/client.key redis-client:/tmp/client.key + kubectl cp --namespace {{ include "common.names.namespace" . }} /path/to/CA.cert redis-client:/tmp/CA.cert + +{{- end }} + + Use the following command to attach to the pod: + + kubectl exec --tty -i redis-client \ + {{- if and (.Values.networkPolicy.enabled) (not .Values.networkPolicy.allowExternal) }}--labels="{{ template "common.names.fullname" . }}-client=true" \{{- end }} + --namespace {{ include "common.names.namespace" . }} -- bash + +2. Connect using the Redis® CLI: + +{{- if eq .Values.architecture "replication" }} + {{- if .Values.sentinel.enabled }} + {{ if .Values.auth.enabled }}REDISCLI_AUTH="$REDIS_PASSWORD" {{ end }}redis-cli -h {{ template "common.names.fullname" . }} -p {{ .Values.sentinel.service.ports.redis }}{{ if .Values.tls.enabled }} --tls --cert /tmp/client.cert --key /tmp/client.key --cacert /tmp/CA.cert{{ end }} # Read only operations + {{ if .Values.auth.enabled }}REDISCLI_AUTH="$REDIS_PASSWORD" {{ end }}redis-cli -h {{ template "common.names.fullname" . }} -p {{ .Values.sentinel.service.ports.sentinel }}{{ if .Values.tls.enabled }} --tls --cert /tmp/client.cert --key /tmp/client.key --cacert /tmp/CA.cert{{ end }} # Sentinel access + {{- else }} + {{ if .Values.auth.enabled }}REDISCLI_AUTH="$REDIS_PASSWORD" {{ end }}redis-cli -h {{ printf "%s-master" (include "common.names.fullname" .) }}{{ if .Values.tls.enabled }} --tls --cert /tmp/client.cert --key /tmp/client.key --cacert /tmp/CA.cert{{ end }} + {{ if .Values.auth.enabled }}REDISCLI_AUTH="$REDIS_PASSWORD" {{ end }}redis-cli -h {{ printf "%s-replicas" (include "common.names.fullname" .) }}{{ if .Values.tls.enabled }} --tls --cert /tmp/client.cert --key /tmp/client.key --cacert /tmp/CA.cert{{ end }} + {{- end }} +{{- else }} + {{ if .Values.auth.enabled }}REDISCLI_AUTH="$REDIS_PASSWORD" {{ end }}redis-cli -h {{ template "common.names.fullname" . }}-master{{ if .Values.tls.enabled }} --tls --cert /tmp/client.cert --key /tmp/client.key --cacert /tmp/CA.cert{{ end }} +{{- end }} + +{{- if and (.Values.networkPolicy.enabled) (not .Values.networkPolicy.allowExternal) }} + +Note: Since NetworkPolicy is enabled, only pods with label {{ template "common.names.fullname" . }}-client=true" will be able to connect to redis. + +{{- else }} + +To connect to your database from outside the cluster execute the following commands: + +{{- if and (eq .Values.architecture "replication") .Values.sentinel.enabled }} +{{- if contains "NodePort" .Values.sentinel.service.type }} + + export NODE_IP=$(kubectl get nodes --namespace {{ include "common.names.namespace" . }} -o jsonpath="{.items[0].status.addresses[0].address}") + export NODE_PORT=$(kubectl get --namespace {{ include "common.names.namespace" . }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "common.names.fullname" . }}) + {{ if .Values.auth.enabled }}REDISCLI_AUTH="$REDIS_PASSWORD" {{ end }}redis-cli -h $NODE_IP -p $NODE_PORT {{- if .Values.tls.enabled }} --tls --cert /tmp/client.cert --key /tmp/client.key --cacert /tmp/CA.cert{{ end }} + +{{- else if contains "LoadBalancer" .Values.sentinel.service.type }} + + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + Watch the status with: 'kubectl get svc --namespace {{ include "common.names.namespace" . }} -w {{ template "common.names.fullname" . }}' + + export SERVICE_IP=$(kubectl get svc --namespace {{ include "common.names.namespace" . }} {{ template "common.names.fullname" . }} --template "{{ "{{ range (index .status.loadBalancer.ingress 0) }}{{ . }}{{ end }}" }}") + {{ if .Values.auth.enabled }}REDISCLI_AUTH="$REDIS_PASSWORD" {{ end }}redis-cli -h $SERVICE_IP -p {{ .Values.sentinel.service.ports.redis }} {{- if .Values.tls.enabled }} --tls --cert /tmp/client.cert --key /tmp/client.key --cacert /tmp/CA.cert{{ end }} + +{{- else if contains "ClusterIP" .Values.sentinel.service.type }} + + kubectl port-forward --namespace {{ include "common.names.namespace" . }} svc/{{ template "common.names.fullname" . }} {{ .Values.sentinel.service.ports.redis }}:{{ .Values.sentinel.service.ports.redis }} & + {{ if .Values.auth.enabled }}REDISCLI_AUTH="$REDIS_PASSWORD" {{ end }}redis-cli -h 127.0.0.1 -p {{ .Values.sentinel.service.ports.redis }} {{- if .Values.tls.enabled }} --tls --cert /tmp/client.cert --key /tmp/client.key --cacert /tmp/CA.cert{{ end }} + +{{- end }} +{{- else }} +{{- if contains "NodePort" .Values.master.service.type }} + + export NODE_IP=$(kubectl get nodes --namespace {{ include "common.names.namespace" . }} -o jsonpath="{.items[0].status.addresses[0].address}") + export NODE_PORT=$(kubectl get --namespace {{ include "common.names.namespace" . }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ printf "%s-master" (include "common.names.fullname" .) }}) + {{ if .Values.auth.enabled }}REDISCLI_AUTH="$REDIS_PASSWORD" {{ end }}redis-cli -h $NODE_IP -p $NODE_PORT {{- if .Values.tls.enabled }} --tls --cert /tmp/client.cert --key /tmp/client.key --cacert /tmp/CA.cert{{ end }} + +{{- else if contains "LoadBalancer" .Values.master.service.type }} + + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + Watch the status with: 'kubectl get svc --namespace {{ include "common.names.namespace" . }} -w {{ template "common.names.fullname" . }}' + + export SERVICE_IP=$(kubectl get svc --namespace {{ include "common.names.namespace" . }} {{ printf "%s-master" (include "common.names.fullname" .) }} --template "{{ "{{ range (index .status.loadBalancer.ingress 0) }}{{ . }}{{ end }}" }}") + {{ if .Values.auth.enabled }}REDISCLI_AUTH="$REDIS_PASSWORD" {{ end }}redis-cli -h $SERVICE_IP -p {{ .Values.master.service.ports.redis }} {{- if .Values.tls.enabled }} --tls --cert /tmp/client.cert --key /tmp/client.key --cacert /tmp/CA.cert{{ end }} + +{{- else if contains "ClusterIP" .Values.master.service.type }} + + kubectl port-forward --namespace {{ include "common.names.namespace" . }} svc/{{ printf "%s-master" (include "common.names.fullname" .) }} {{ .Values.master.service.ports.redis }}:{{ .Values.master.service.ports.redis }} & + {{ if .Values.auth.enabled }}REDISCLI_AUTH="$REDIS_PASSWORD" {{ end }}redis-cli -h 127.0.0.1 -p {{ .Values.master.service.ports.redis }} {{- if .Values.tls.enabled }} --tls --cert /tmp/client.cert --key /tmp/client.key --cacert /tmp/CA.cert{{ end }} + +{{- end }} +{{- end }} + +{{- end }} +{{- end }} +{{- include "redis.checkRollingTags" . }} +{{- include "common.warnings.rollingTag" .Values.volumePermissions.image }} +{{- include "common.warnings.rollingTag" .Values.sysctl.image }} +{{- include "redis.validateValues" . }} + +{{- if and (eq .Values.architecture "replication") .Values.sentinel.enabled (eq .Values.sentinel.service.type "NodePort") (not .Release.IsUpgrade ) }} +{{- if $.Values.sentinel.service.nodePorts.sentinel }} +No need to upgrade, ports and nodeports have been set from values +{{- else }} +#!#!#!#!#!#!#!# IMPORTANT #!#!#!#!#!#!#!# +YOU NEED TO PERFORM AN UPGRADE FOR THE SERVICES AND WORKLOAD TO BE CREATED +{{- end }} +{{- end }} +{{- $resourceSections := list "metrics" "replica" "sentinel" "sysctl" "volumePermissions" }} +{{- if not (and (eq .Values.architecture "replication") .Values.sentinel.enabled) }} + {{- $resourceSections = append $resourceSections "master" -}} +{{- end }} +{{- include "common.warnings.resources" (dict "sections" $resourceSections "context" $) }} +{{- include "common.warnings.modifiedImages" (dict "images" (list .Values.image .Values.sentinel.image .Values.metrics.image .Values.volumePermissions.image .Values.kubectl.image .Values.sysctl.image) "context" $) }} +{{- include "common.errors.insecureImages" (dict "images" (list .Values.image .Values.sentinel.image .Values.metrics.image .Values.volumePermissions.image .Values.kubectl.image .Values.sysctl.image) "context" $) }} diff --git a/charts/open-webui/charts/redis/templates/_helpers.tpl b/charts/open-webui/charts/redis/templates/_helpers.tpl new file mode 100644 index 0000000..43ad1a4 --- /dev/null +++ b/charts/open-webui/charts/redis/templates/_helpers.tpl @@ -0,0 +1,325 @@ +{{/* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{/* vim: set filetype=mustache: */}} + +{{/* +Return the proper Redis image name +*/}} +{{- define "redis.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper Redis Sentinel image name +*/}} +{{- define "redis.sentinel.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.sentinel.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper image name (for the metrics image) +*/}} +{{- define "redis.metrics.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.metrics.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper image name (for the init container volume-permissions image) +*/}} +{{- define "redis.volumePermissions.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.volumePermissions.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return kubectl image +*/}} +{{- define "redis.kubectl.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.kubectl.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return sysctl image +*/}} +{{- define "redis.sysctl.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.sysctl.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names +*/}} +{{- define "redis.imagePullSecrets" -}} +{{- include "common.images.renderPullSecrets" (dict "images" (list .Values.image .Values.sentinel.image .Values.metrics.image .Values.volumePermissions.image .Values.sysctl.image) "context" $) -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for networkpolicy. +*/}} +{{- define "networkPolicy.apiVersion" -}} +{{- if semverCompare ">=1.4-0, <1.7-0" .Capabilities.KubeVersion.GitVersion -}} +{{- print "extensions/v1beta1" -}} +{{- else -}} +{{- print "networking.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiGroup for PodSecurityPolicy. +*/}} +{{- define "podSecurityPolicy.apiGroup" -}} +{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +{{- print "policy" -}} +{{- else -}} +{{- print "extensions" -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if a TLS secret object should be created +*/}} +{{- define "redis.createTlsSecret" -}} +{{- if and .Values.tls.enabled .Values.tls.autoGenerated (and (not .Values.tls.existingSecret) (not .Values.tls.certificatesSecret)) }} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return the secret containing Redis TLS certificates +*/}} +{{- define "redis.tlsSecretName" -}} +{{- $secretName := coalesce .Values.tls.existingSecret .Values.tls.certificatesSecret -}} +{{- if $secretName -}} + {{- printf "%s" (tpl $secretName $) -}} +{{- else -}} + {{- printf "%s-crt" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return the path to the cert file. +*/}} +{{- define "redis.tlsCert" -}} +{{- if (include "redis.createTlsSecret" . ) -}} + {{- printf "/opt/bitnami/redis/certs/%s" "tls.crt" -}} +{{- else -}} + {{- required "Certificate filename is required when TLS in enabled" .Values.tls.certFilename | printf "/opt/bitnami/redis/certs/%s" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the path to the cert key file. +*/}} +{{- define "redis.tlsCertKey" -}} +{{- if (include "redis.createTlsSecret" . ) -}} + {{- printf "/opt/bitnami/redis/certs/%s" "tls.key" -}} +{{- else -}} + {{- required "Certificate Key filename is required when TLS in enabled" .Values.tls.certKeyFilename | printf "/opt/bitnami/redis/certs/%s" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the path to the CA cert file. +*/}} +{{- define "redis.tlsCACert" -}} +{{- if (include "redis.createTlsSecret" . ) -}} + {{- printf "/opt/bitnami/redis/certs/%s" "ca.crt" -}} +{{- else }} + {{- ternary "" (printf "/opt/bitnami/redis/certs/%s" .Values.tls.certCAFilename) (empty .Values.tls.certCAFilename) }} +{{- end -}} +{{- end -}} + +{{/* +Return the path to the DH params file. +*/}} +{{- define "redis.tlsDHParams" -}} +{{- if .Values.tls.dhParamsFilename -}} +{{- printf "/opt/bitnami/redis/certs/%s" .Values.tls.dhParamsFilename -}} +{{- end -}} +{{- end -}} + +{{/* +Create the name of the shared service account to use +*/}} +{{- define "redis.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "common.names.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Create the name of the master service account to use +*/}} +{{- define "redis.masterServiceAccountName" -}} +{{- if .Values.master.serviceAccount.create -}} + {{ default (printf "%s-master" (include "common.names.fullname" .)) .Values.master.serviceAccount.name }} +{{- else -}} + {{- if .Values.serviceAccount.create -}} + {{ template "redis.serviceAccountName" . }} + {{- else -}} + {{ default "default" .Values.master.serviceAccount.name }} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create the name of the replicas service account to use +*/}} +{{- define "redis.replicaServiceAccountName" -}} +{{- if .Values.replica.serviceAccount.create -}} + {{ default (printf "%s-replica" (include "common.names.fullname" .)) .Values.replica.serviceAccount.name }} +{{- else -}} + {{- if .Values.serviceAccount.create -}} + {{ template "redis.serviceAccountName" . }} + {{- else -}} + {{ default "default" .Values.replica.serviceAccount.name }} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the configuration configmap name +*/}} +{{- define "redis.configmapName" -}} +{{- if .Values.existingConfigmap -}} + {{- printf "%s" (tpl .Values.existingConfigmap $) -}} +{{- else -}} + {{- printf "%s-configuration" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if a configmap object should be created +*/}} +{{- define "redis.createConfigmap" -}} +{{- if empty .Values.existingConfigmap }} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Get the password secret. +*/}} +{{- define "redis.secretName" -}} +{{- if .Values.auth.existingSecret -}} +{{- printf "%s" (tpl .Values.auth.existingSecret $) -}} +{{- else -}} +{{- printf "%s" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Get the password key to be retrieved from Redis® secret. +*/}} +{{- define "redis.secretPasswordKey" -}} +{{- if and .Values.auth.existingSecret .Values.auth.existingSecretPasswordKey -}} +{{- printf "%s" (tpl .Values.auth.existingSecretPasswordKey $) -}} +{{- else -}} +{{- printf "redis-password" -}} +{{- end -}} +{{- end -}} + +{{/* +Return Redis® password +*/}} +{{- define "redis.password" -}} +{{- if or .Values.auth.enabled .Values.global.redis.password -}} + {{- include "common.secrets.passwords.manage" (dict "secret" (include "redis.secretName" .) "key" (include "redis.secretPasswordKey" .) "providedValues" (list "global.redis.password" "auth.password") "length" 10 "skipB64enc" true "skipQuote" true "honorProvidedValues" true "context" $) -}} +{{- end }} +{{- end }} + +{{/* Check if there are rolling tags in the images */}} +{{- define "redis.checkRollingTags" -}} +{{- include "common.warnings.rollingTag" .Values.image }} +{{- include "common.warnings.rollingTag" .Values.sentinel.image }} +{{- include "common.warnings.rollingTag" .Values.metrics.image }} +{{- end -}} + +{{/* +Compile all warnings into a single message, and call fail. +*/}} +{{- define "redis.validateValues" -}} +{{- $messages := list -}} +{{- $messages := append $messages (include "redis.validateValues.topologySpreadConstraints" .) -}} +{{- $messages := append $messages (include "redis.validateValues.architecture" .) -}} +{{- $messages := append $messages (include "redis.validateValues.podSecurityPolicy.create" .) -}} +{{- $messages := append $messages (include "redis.validateValues.tls" .) -}} +{{- $messages := append $messages (include "redis.validateValues.createMaster" .) -}} +{{- $messages := without $messages "" -}} +{{- $message := join "\n" $messages -}} + +{{- if $message -}} +{{- printf "\nVALUES VALIDATION:\n%s" $message | fail -}} +{{- end -}} +{{- end -}} + +{{/* Validate values of Redis® - spreadConstrainsts K8s version */}} +{{- define "redis.validateValues.topologySpreadConstraints" -}} +{{- if and (semverCompare "<1.16-0" .Capabilities.KubeVersion.GitVersion) .Values.replica.topologySpreadConstraints -}} +redis: topologySpreadConstraints + Pod Topology Spread Constraints are only available on K8s >= 1.16 + Find more information at https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ +{{- end -}} +{{- end -}} + +{{/* Validate values of Redis® - must provide a valid architecture */}} +{{- define "redis.validateValues.architecture" -}} +{{- if and (ne .Values.architecture "standalone") (ne .Values.architecture "replication") -}} +redis: architecture + Invalid architecture selected. Valid values are "standalone" and + "replication". Please set a valid architecture (--set architecture="xxxx") +{{- end -}} +{{- if and .Values.sentinel.enabled (not (eq .Values.architecture "replication")) }} +redis: architecture + Using redis sentinel on standalone mode is not supported. + To deploy redis sentinel, please select the "replication" mode + (--set "architecture=replication,sentinel.enabled=true") +{{- end -}} +{{- end -}} + +{{/* Validate values of Redis® - PodSecurityPolicy create */}} +{{- define "redis.validateValues.podSecurityPolicy.create" -}} +{{- if and .Values.podSecurityPolicy.create (not .Values.podSecurityPolicy.enabled) }} +redis: podSecurityPolicy.create + In order to create PodSecurityPolicy, you also need to enable + podSecurityPolicy.enabled field +{{- end -}} +{{- end -}} + +{{/* Validate values of Redis® - TLS enabled */}} +{{- define "redis.validateValues.tls" -}} +{{- if and .Values.tls.enabled (not .Values.tls.autoGenerated) (not .Values.tls.existingSecret) (not .Values.tls.certificatesSecret) }} +redis: tls.enabled + In order to enable TLS, you also need to provide + an existing secret containing the TLS certificates or + enable auto-generated certificates. +{{- end -}} +{{- end -}} + +{{/* Validate values of Redis® - master service enabled */}} +{{- define "redis.validateValues.createMaster" -}} +{{- if and (or .Values.sentinel.masterService.enabled .Values.sentinel.service.createMaster) (or (not .Values.rbac.create) (not .Values.replica.automountServiceAccountToken) (not .Values.serviceAccount.create)) }} +redis: sentinel.masterService.enabled + In order to redirect requests only to the master pod via the service, you also need to + create rbac and serviceAccount. In addition, you need to enable + replica.automountServiceAccountToken. +{{- end -}} +{{- end -}} + +{{/* Define the suffix utilized for external-dns */}} +{{- define "redis.externalDNS.suffix" -}} +{{ printf "%s.%s" (include "common.names.fullname" .) .Values.useExternalDNS.suffix }} +{{- end -}} + +{{/* Compile all annotations utilized for external-dns */}} +{{- define "redis.externalDNS.annotations" -}} +{{- if and .Values.useExternalDNS.enabled .Values.useExternalDNS.annotationKey }} +{{ .Values.useExternalDNS.annotationKey }}hostname: {{ include "redis.externalDNS.suffix" . }} +{{- range $key, $val := .Values.useExternalDNS.additionalAnnotations }} +{{ $.Values.useExternalDNS.annotationKey }}{{ $key }}: {{ $val | quote }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/configmap.yaml b/charts/open-webui/charts/redis/templates/configmap.yaml new file mode 100644 index 0000000..22df358 --- /dev/null +++ b/charts/open-webui/charts/redis/templates/configmap.yaml @@ -0,0 +1,64 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if (include "redis.createConfigmap" .) }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ printf "%s-configuration" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +data: + redis.conf: |- + # User-supplied common configuration: + {{- if .Values.commonConfiguration }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonConfiguration "context" $ ) | nindent 4 }} + {{- end }} + # End of common configuration + master.conf: |- + dir {{ .Values.master.persistence.path }} + # User-supplied master configuration: + {{- if .Values.master.configuration }} + {{- include "common.tplvalues.render" ( dict "value" .Values.master.configuration "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.master.disableCommands }} + {{- range .Values.master.disableCommands }} + rename-command {{ . }} "" + {{- end }} + {{- end }} + # End of master configuration + replica.conf: |- + dir {{ .Values.replica.persistence.path }} + # User-supplied replica configuration: + {{- if .Values.replica.configuration }} + {{- include "common.tplvalues.render" ( dict "value" .Values.replica.configuration "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.replica.disableCommands }} + {{- range .Values.replica.disableCommands }} + rename-command {{ . }} "" + {{- end }} + {{- end }} + # End of replica configuration + {{- if .Values.sentinel.enabled }} + sentinel.conf: |- + dir "/tmp" + port {{ .Values.sentinel.containerPorts.sentinel }} + sentinel monitor {{ .Values.sentinel.masterSet }} {{ template "common.names.fullname" . }}-node-0.{{ template "common.names.fullname" . }}-headless.{{ include "common.names.namespace" . }}.svc.{{ .Values.clusterDomain }} {{ .Values.sentinel.service.ports.redis }} {{ .Values.sentinel.quorum }} + sentinel down-after-milliseconds {{ .Values.sentinel.masterSet }} {{ .Values.sentinel.downAfterMilliseconds }} + sentinel failover-timeout {{ .Values.sentinel.masterSet }} {{ .Values.sentinel.failoverTimeout }} + sentinel parallel-syncs {{ .Values.sentinel.masterSet }} {{ .Values.sentinel.parallelSyncs }} + {{- if or .Values.sentinel.masterService.enabled .Values.sentinel.service.createMaster }} + sentinel client-reconfig-script {{ .Values.sentinel.masterSet }} /opt/bitnami/scripts/start-scripts/push-master-label.sh + {{- end }} + # User-supplied sentinel configuration: + {{- if .Values.sentinel.configuration }} + {{- include "common.tplvalues.render" ( dict "value" .Values.sentinel.configuration "context" $ ) | nindent 4 }} + {{- end }} + # End of sentinel configuration + {{- end }} +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/extra-list.yaml b/charts/open-webui/charts/redis/templates/extra-list.yaml new file mode 100644 index 0000000..329f5c6 --- /dev/null +++ b/charts/open-webui/charts/redis/templates/extra-list.yaml @@ -0,0 +1,9 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- range .Values.extraDeploy }} +--- +{{ include "common.tplvalues.render" (dict "value" . "context" $) }} +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/headless-svc.yaml b/charts/open-webui/charts/redis/templates/headless-svc.yaml new file mode 100644 index 0000000..bfdb1a6 --- /dev/null +++ b/charts/open-webui/charts/redis/templates/headless-svc.yaml @@ -0,0 +1,38 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +apiVersion: v1 +kind: Service +metadata: + name: {{ printf "%s-headless" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if or .Values.sentinel.service.headless.annotations .Values.commonAnnotations (include "redis.externalDNS.annotations" .) }} + annotations: + {{- if or .Values.sentinel.service.headless.annotations .Values.commonAnnotations }} + {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list .Values.sentinel.service.headless.annotations .Values.commonAnnotations ) "context" . ) }} + {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} + {{- end }} + {{- include "redis.externalDNS.annotations" . | nindent 4 }} + {{- end }} +spec: + type: ClusterIP + clusterIP: None + {{- if .Values.sentinel.enabled }} + publishNotReadyAddresses: true + {{- end }} + ports: + - name: tcp-redis + port: {{ if .Values.sentinel.enabled }}{{ .Values.sentinel.service.ports.redis }}{{ else }}{{ .Values.master.service.ports.redis }}{{ end }} + targetPort: redis + {{- if .Values.sentinel.enabled }} + - name: tcp-sentinel + port: {{ .Values.sentinel.service.ports.sentinel }} + targetPort: redis-sentinel + {{- end }} + {{- if .Values.sentinel.service.headless.extraPorts }} + {{- include "common.tplvalues.render" (dict "value" .Values.sentinel.service.headless.extraPorts "context" $) | nindent 4 }} + {{- end }} + selector: {{- include "common.labels.matchLabels" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} diff --git a/charts/open-webui/charts/redis/templates/health-configmap.yaml b/charts/open-webui/charts/redis/templates/health-configmap.yaml new file mode 100644 index 0000000..e84cbf7 --- /dev/null +++ b/charts/open-webui/charts/redis/templates/health-configmap.yaml @@ -0,0 +1,204 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ printf "%s-health" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +data: + ping_readiness_local.sh: |- + #!/bin/bash + + [[ -f $REDIS_PASSWORD_FILE ]] && export REDIS_PASSWORD="$(< "${REDIS_PASSWORD_FILE}")" + [[ -n "$REDIS_PASSWORD" ]] && export REDISCLI_AUTH="$REDIS_PASSWORD" + response=$( + timeout -s 15 $1 \ + redis-cli \ + -h localhost \ +{{- if .Values.tls.enabled }} + -p $REDIS_TLS_PORT \ + --tls \ + {{- if not (empty (include "redis.tlsCACert" .)) }} + --cacert {{ template "redis.tlsCACert" . }} \ + {{- end }} + {{- if .Values.tls.authClients }} + --cert {{ template "redis.tlsCert" . }} \ + --key {{ template "redis.tlsCertKey" . }} \ + {{- end }} +{{- else }} + -p $REDIS_PORT \ +{{- end }} + ping + ) + if [ "$?" -eq "124" ]; then + echo "Timed out" + exit 1 + fi + if [ "$response" != "PONG" ]; then + echo "$response" + exit 1 + fi + ping_liveness_local.sh: |- + #!/bin/bash + + [[ -f $REDIS_PASSWORD_FILE ]] && export REDIS_PASSWORD="$(< "${REDIS_PASSWORD_FILE}")" + [[ -n "$REDIS_PASSWORD" ]] && export REDISCLI_AUTH="$REDIS_PASSWORD" + response=$( + timeout -s 15 $1 \ + redis-cli \ + -h localhost \ +{{- if .Values.tls.enabled }} + -p $REDIS_TLS_PORT \ + --tls \ + {{- if not (empty (include "redis.tlsCACert" .)) }} + --cacert {{ template "redis.tlsCACert" . }} \ + {{- end }} + {{- if .Values.tls.authClients }} + --cert {{ template "redis.tlsCert" . }} \ + --key {{ template "redis.tlsCertKey" . }} \ + {{- end }} +{{- else }} + -p $REDIS_PORT \ +{{- end }} + ping + ) + if [ "$?" -eq "124" ]; then + echo "Timed out" + exit 1 + fi + responseFirstWord=$(echo $response | head -n1 | awk '{print $1;}') + if [ "$response" != "PONG" ] && [ "$responseFirstWord" != "LOADING" ] && [ "$responseFirstWord" != "MASTERDOWN" ]; then + echo "$response" + exit 1 + fi +{{- if .Values.sentinel.enabled }} + ping_sentinel.sh: |- + #!/bin/bash + +{{- if .Values.auth.sentinel }} + [[ -f $REDIS_PASSWORD_FILE ]] && export REDIS_PASSWORD="$(< "${REDIS_PASSWORD_FILE}")" + [[ -n "$REDIS_PASSWORD" ]] && export REDISCLI_AUTH="$REDIS_PASSWORD" +{{- end }} + response=$( + timeout -s 15 $1 \ + redis-cli \ + -h localhost \ +{{- if .Values.tls.enabled }} + -p $REDIS_SENTINEL_TLS_PORT_NUMBER \ + --tls \ + {{- if not (empty (include "redis.tlsCACert" .)) }} + --cacert "$REDIS_SENTINEL_TLS_CA_FILE" \ + {{- end }} + {{- if .Values.tls.authClients }} + --cert "$REDIS_SENTINEL_TLS_CERT_FILE" \ + --key "$REDIS_SENTINEL_TLS_KEY_FILE" \ + {{- end }} +{{- else }} + -p $REDIS_SENTINEL_PORT \ +{{- end }} + ping + ) + if [ "$?" -eq "124" ]; then + echo "Timed out" + exit 1 + fi + if [ "$response" != "PONG" ]; then + echo "$response" + exit 1 + fi + parse_sentinels.awk: |- + /ip/ {FOUND_IP=1} + /port/ {FOUND_PORT=1} + /runid/ {FOUND_RUNID=1} + !/ip|port|runid/ { + if (FOUND_IP==1) { + IP=$1; FOUND_IP=0; + } + else if (FOUND_PORT==1) { + PORT=$1; + FOUND_PORT=0; + } else if (FOUND_RUNID==1) { + printf "\nsentinel known-sentinel {{ .Values.sentinel.masterSet }} %s %s %s", IP, PORT, $0; FOUND_RUNID=0; + } + } +{{- end }} + ping_readiness_master.sh: |- + #!/bin/bash + + [[ -f $REDIS_MASTER_PASSWORD_FILE ]] && export REDIS_MASTER_PASSWORD="$(< "${REDIS_MASTER_PASSWORD_FILE}")" + [[ -n "$REDIS_MASTER_PASSWORD" ]] && export REDISCLI_AUTH="$REDIS_MASTER_PASSWORD" + response=$( + timeout -s 15 $1 \ + redis-cli \ + -h $REDIS_MASTER_HOST \ + -p $REDIS_MASTER_PORT_NUMBER \ +{{- if .Values.tls.enabled }} + --tls \ + {{- if not (empty (include "redis.tlsCACert" .)) }} + --cacert {{ template "redis.tlsCACert" . }} \ + {{- end }} + {{- if .Values.tls.authClients }} + --cert {{ template "redis.tlsCert" . }} \ + --key {{ template "redis.tlsCertKey" . }} \ + {{- end }} +{{- end }} + ping + ) + if [ "$?" -eq "124" ]; then + echo "Timed out" + exit 1 + fi + if [ "$response" != "PONG" ]; then + echo "$response" + exit 1 + fi + ping_liveness_master.sh: |- + #!/bin/bash + + [[ -f $REDIS_MASTER_PASSWORD_FILE ]] && export REDIS_MASTER_PASSWORD="$(< "${REDIS_MASTER_PASSWORD_FILE}")" + [[ -n "$REDIS_MASTER_PASSWORD" ]] && export REDISCLI_AUTH="$REDIS_MASTER_PASSWORD" + response=$( + timeout -s 15 $1 \ + redis-cli \ + -h $REDIS_MASTER_HOST \ + -p $REDIS_MASTER_PORT_NUMBER \ +{{- if .Values.tls.enabled }} + --tls \ + {{- if not (empty (include "redis.tlsCACert" .)) }} + --cacert {{ template "redis.tlsCACert" . }} \ + {{- end }} + {{- if .Values.tls.authClients }} + --cert {{ template "redis.tlsCert" . }} \ + --key {{ template "redis.tlsCertKey" . }} \ + {{- end }} +{{- end }} + ping + ) + if [ "$?" -eq "124" ]; then + echo "Timed out" + exit 1 + fi + responseFirstWord=$(echo $response | head -n1 | awk '{print $1;}') + if [ "$response" != "PONG" ] && [ "$responseFirstWord" != "LOADING" ]; then + echo "$response" + exit 1 + fi + ping_readiness_local_and_master.sh: |- + script_dir="$(dirname "$0")" + exit_status=0 + "$script_dir/ping_readiness_local.sh" $1 || exit_status=$? + "$script_dir/ping_readiness_master.sh" $1 || exit_status=$? + exit $exit_status + ping_liveness_local_and_master.sh: |- + script_dir="$(dirname "$0")" + exit_status=0 + "$script_dir/ping_liveness_local.sh" $1 || exit_status=$? + "$script_dir/ping_liveness_master.sh" $1 || exit_status=$? + exit $exit_status diff --git a/charts/open-webui/charts/redis/templates/master/application.yaml b/charts/open-webui/charts/redis/templates/master/application.yaml new file mode 100644 index 0000000..7218626 --- /dev/null +++ b/charts/open-webui/charts/redis/templates/master/application.yaml @@ -0,0 +1,558 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if gt (int64 .Values.master.count) 0 -}} +{{- if or (not (eq .Values.architecture "replication")) (not .Values.sentinel.enabled) }} +apiVersion: {{ include "common.capabilities.statefulset.apiVersion" . }} +kind: {{ .Values.master.kind }} +metadata: + name: {{ printf "%s-master" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: master + {{- if or .Values.commonAnnotations .Values.master.annotations }} + {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list .Values.master.annotations .Values.commonAnnotations ) "context" . ) }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} + {{- end }} +spec: + {{- if not (eq .Values.master.kind "DaemonSet") }} + replicas: {{ .Values.master.count }} + {{- end }} + revisionHistoryLimit: {{ .Values.master.revisionHistoryLimit }} + {{- $podLabels := include "common.tplvalues.merge" ( dict "values" ( list .Values.master.podLabels .Values.commonLabels ) "context" . ) }} + selector: + matchLabels: {{- include "common.labels.matchLabels" ( dict "customLabels" $podLabels "context" $ ) | nindent 6 }} + app.kubernetes.io/component: master + {{- if (eq .Values.master.kind "StatefulSet") }} + serviceName: {{ printf "%s-headless" (include "common.names.fullname" .) }} + {{- end }} + {{- if .Values.master.updateStrategy }} + {{- if (eq .Values.master.kind "Deployment") }} + strategy: {{- toYaml .Values.master.updateStrategy | nindent 4 }} + {{- else }} + updateStrategy: {{- toYaml .Values.master.updateStrategy | nindent 4 }} + {{- end }} + {{- if and .Values.master.minReadySeconds (semverCompare ">= 1.23-0" (include "common.capabilities.kubeVersion" .)) }} + minReadySeconds: {{ .Values.master.minReadySeconds }} + {{- end }} + {{- end }} + template: + metadata: + labels: {{- include "common.labels.standard" ( dict "customLabels" $podLabels "context" $ ) | nindent 8 }} + app.kubernetes.io/component: master + {{- if and .Values.metrics.enabled .Values.metrics.podLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.podLabels "context" $ ) | nindent 8 }} + {{- end }} + annotations: + {{- if (include "redis.createConfigmap" .) }} + checksum/configmap: {{ pick ( include (print $.Template.BasePath "/configmap.yaml") . | fromYaml ) "data" | toYaml | sha256sum }} + {{- end }} + checksum/health: {{ pick ( include (print $.Template.BasePath "/health-configmap.yaml") . | fromYaml ) "data" | toYaml | sha256sum }} + checksum/scripts: {{ pick ( include (print $.Template.BasePath "/scripts-configmap.yaml") . | fromYaml ) "data" | toYaml | sha256sum }} + checksum/secret: {{ pick ( include (print $.Template.BasePath "/secret.yaml") . | fromYaml ) "data" | toYaml | sha256sum }} + {{- if .Values.master.podAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.master.podAnnotations "context" $ ) | nindent 8 }} + {{- end }} + {{- if and .Values.metrics.enabled .Values.metrics.podAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.podAnnotations "context" $ ) | nindent 8 }} + {{- end }} + spec: + {{- if .Values.master.extraPodSpec }} + {{- include "common.tplvalues.render" (dict "value" .Values.master.extraPodSpec "context" $) | nindent 6 }} + {{- end }} + {{- include "redis.imagePullSecrets" . | nindent 6 }} + {{- if .Values.master.hostAliases }} + hostAliases: {{- include "common.tplvalues.render" (dict "value" .Values.master.hostAliases "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.master.podSecurityContext.enabled }} + securityContext: {{- include "common.compatibility.renderSecurityContext" (dict "secContext" .Values.master.podSecurityContext "context" $) | nindent 8 }} + {{- end }} + serviceAccountName: {{ template "redis.masterServiceAccountName" . }} + automountServiceAccountToken: {{ .Values.master.automountServiceAccountToken }} + {{- if .Values.master.priorityClassName }} + priorityClassName: {{ .Values.master.priorityClassName | quote }} + {{- end }} + {{- if .Values.master.affinity }} + affinity: {{- include "common.tplvalues.render" (dict "value" .Values.master.affinity "context" $) | nindent 8 }} + {{- else }} + affinity: + podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.master.podAffinityPreset "component" "master" "customLabels" $podLabels "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.master.podAntiAffinityPreset "component" "master" "customLabels" $podLabels "context" $) | nindent 10 }} + nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.master.nodeAffinityPreset.type "key" .Values.master.nodeAffinityPreset.key "values" .Values.master.nodeAffinityPreset.values) | nindent 10 }} + {{- end }} + {{- if .Values.master.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.master.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.master.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.master.tolerations "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.master.topologySpreadConstraints }} + topologySpreadConstraints: {{- include "common.tplvalues.render" (dict "value" .Values.master.topologySpreadConstraints "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.master.shareProcessNamespace }} + shareProcessNamespace: {{ .Values.master.shareProcessNamespace }} + {{- end }} + {{- if .Values.master.schedulerName }} + schedulerName: {{ .Values.master.schedulerName | quote }} + {{- end }} + {{- if .Values.master.dnsPolicy }} + dnsPolicy: {{ .Values.master.dnsPolicy }} + {{- end }} + {{- if .Values.master.dnsConfig }} + dnsConfig: {{- include "common.tplvalues.render" (dict "value" .Values.master.dnsConfig "context" $) | nindent 8 }} + {{- end }} + enableServiceLinks: {{ .Values.master.enableServiceLinks }} + terminationGracePeriodSeconds: {{ .Values.master.terminationGracePeriodSeconds }} + containers: + - name: redis + image: {{ template "redis.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + {{- if .Values.master.lifecycleHooks }} + lifecycle: {{- include "common.tplvalues.render" (dict "value" .Values.master.lifecycleHooks "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.master.containerSecurityContext.enabled }} + securityContext: {{- include "common.compatibility.renderSecurityContext" (dict "secContext" .Values.master.containerSecurityContext "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} + {{- else if .Values.master.command }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.master.command "context" $) | nindent 12 }} + {{- else }} + command: + - /bin/bash + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} + {{- else if .Values.master.args }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.master.args "context" $) | nindent 12 }} + {{- else }} + args: + - -c + - /opt/bitnami/scripts/start-scripts/start-master.sh + {{- end }} + env: + - name: BITNAMI_DEBUG + value: {{ ternary "true" "false" (or .Values.image.debug .Values.diagnosticMode.enabled) | quote }} + - name: REDIS_REPLICATION_MODE + value: master + - name: ALLOW_EMPTY_PASSWORD + value: {{ ternary "no" "yes" .Values.auth.enabled | quote }} + {{- if .Values.auth.enabled }} + {{- if .Values.auth.usePasswordFiles }} + - name: REDIS_PASSWORD_FILE + value: "/opt/bitnami/redis/secrets/redis-password" + {{- else }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "redis.secretName" . }} + key: {{ template "redis.secretPasswordKey" . }} + {{- end }} + {{- end }} + - name: REDIS_TLS_ENABLED + value: {{ ternary "yes" "no" .Values.tls.enabled | quote }} + {{- if .Values.tls.enabled }} + - name: REDIS_TLS_PORT + value: {{ .Values.master.containerPorts.redis | quote }} + - name: REDIS_TLS_AUTH_CLIENTS + value: {{ ternary "yes" "no" .Values.tls.authClients | quote }} + - name: REDIS_TLS_CERT_FILE + value: {{ template "redis.tlsCert" . }} + - name: REDIS_TLS_KEY_FILE + value: {{ template "redis.tlsCertKey" . }} + {{- if not (empty (include "redis.tlsCACert" .)) }} + - name: REDIS_TLS_CA_FILE + value: {{ template "redis.tlsCACert" . }} + {{- end }} + {{- if .Values.tls.dhParamsFilename }} + - name: REDIS_TLS_DH_PARAMS_FILE + value: {{ template "redis.tlsDHParams" . }} + {{- end }} + {{- else }} + - name: REDIS_PORT + value: {{ .Values.master.containerPorts.redis | quote }} + {{- end }} + {{- if .Values.master.extraEnvVars }} + {{- include "common.tplvalues.render" (dict "value" .Values.master.extraEnvVars "context" $) | nindent 12 }} + {{- end }} + {{- if or .Values.master.extraEnvVarsCM .Values.master.extraEnvVarsSecret }} + envFrom: + {{- if .Values.master.extraEnvVarsCM }} + - configMapRef: + name: {{ .Values.master.extraEnvVarsCM }} + {{- end }} + {{- if .Values.master.extraEnvVarsSecret }} + - secretRef: + name: {{ .Values.master.extraEnvVarsSecret }} + {{- end }} + {{- end }} + ports: + - name: redis + containerPort: {{ .Values.master.containerPorts.redis }} + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.master.customStartupProbe }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.master.customStartupProbe "context" $) | nindent 12 }} + {{- else if .Values.master.startupProbe.enabled }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.master.startupProbe "enabled") "context" $) | nindent 12 }} + tcpSocket: + port: redis + {{- end }} + {{- if .Values.master.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.master.customLivenessProbe "context" $) | nindent 12 }} + {{- else if .Values.master.livenessProbe.enabled }} + livenessProbe: + initialDelaySeconds: {{ .Values.master.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.master.livenessProbe.periodSeconds }} + # One second longer than command timeout should prevent generation of zombie processes. + timeoutSeconds: {{ add1 .Values.master.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.master.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.master.livenessProbe.failureThreshold }} + exec: + command: + - sh + - -c + - /health/ping_liveness_local.sh {{ .Values.master.livenessProbe.timeoutSeconds }} + {{- end }} + {{- if .Values.master.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.master.customReadinessProbe "context" $) | nindent 12 }} + {{- else if .Values.master.readinessProbe.enabled }} + readinessProbe: + initialDelaySeconds: {{ .Values.master.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.master.readinessProbe.periodSeconds }} + timeoutSeconds: {{ add1 .Values.master.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.master.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.master.readinessProbe.failureThreshold }} + exec: + command: + - sh + - -c + - /health/ping_readiness_local.sh {{ .Values.master.readinessProbe.timeoutSeconds }} + {{- end }} + {{- end }} + {{- if .Values.master.resources }} + resources: {{- toYaml .Values.master.resources | nindent 12 }} + {{- else if ne .Values.master.resourcesPreset "none" }} + resources: {{- include "common.resources.preset" (dict "type" .Values.master.resourcesPreset) | nindent 12 }} + {{- end }} + volumeMounts: + - name: start-scripts + mountPath: /opt/bitnami/scripts/start-scripts + - name: health + mountPath: /health + {{- if .Values.auth.usePasswordFiles }} + - name: redis-password + mountPath: /opt/bitnami/redis/secrets/ + {{- end }} + - name: redis-data + mountPath: {{ .Values.master.persistence.path }} + {{- if .Values.master.persistence.subPath }} + subPath: {{ .Values.master.persistence.subPath }} + {{- else if .Values.master.persistence.subPathExpr }} + subPathExpr: {{ .Values.master.persistence.subPathExpr }} + {{- end }} + - name: config + mountPath: /opt/bitnami/redis/mounted-etc + - name: empty-dir + mountPath: /opt/bitnami/redis/etc/ + subPath: app-conf-dir + - name: empty-dir + mountPath: /tmp + subPath: tmp-dir + {{- if .Values.tls.enabled }} + - name: redis-certificates + mountPath: /opt/bitnami/redis/certs + readOnly: true + {{- end }} + {{- if .Values.master.extraVolumeMounts }} + {{- include "common.tplvalues.render" ( dict "value" .Values.master.extraVolumeMounts "context" $ ) | nindent 12 }} + {{- end }} + {{- if .Values.metrics.enabled }} + - name: metrics + image: {{ include "redis.metrics.image" . }} + imagePullPolicy: {{ .Values.metrics.image.pullPolicy | quote }} + {{- if .Values.metrics.containerSecurityContext.enabled }} + securityContext: {{- include "common.compatibility.renderSecurityContext" (dict "secContext" .Values.metrics.containerSecurityContext "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} + {{- else if .Values.metrics.command }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.command "context" $) | nindent 12 }} + {{- else }} + command: + - /bin/bash + - -c + - | + if [[ -f '/secrets/redis-password' ]]; then + export REDIS_PASSWORD=$(cat /secrets/redis-password) + fi + redis_exporter{{- range $key, $value := .Values.metrics.extraArgs }} --{{ $key }}={{ $value }}{{- end }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} + {{- end }} + env: + - name: REDIS_ALIAS + value: {{ template "common.names.fullname" . }} + - name: REDIS_EXPORTER_WEB_LISTEN_ADDRESS + value: {{ printf ":%v" .Values.metrics.containerPorts.http }} + {{- if .Values.auth.enabled }} + - name: REDIS_USER + value: default + {{- if (not .Values.auth.usePasswordFiles) }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "redis.secretName" . }} + key: {{ template "redis.secretPasswordKey" . }} + {{- end }} + {{- end }} + {{- if .Values.tls.enabled }} + - name: REDIS_ADDR + value: rediss://{{ .Values.metrics.redisTargetHost }}:{{ .Values.master.containerPorts.redis }} + {{- if .Values.tls.authClients }} + - name: REDIS_EXPORTER_TLS_CLIENT_KEY_FILE + value: {{ template "redis.tlsCertKey" . }} + - name: REDIS_EXPORTER_TLS_CLIENT_CERT_FILE + value: {{ template "redis.tlsCert" . }} + {{- end }} + {{- if not (empty (include "redis.tlsCACert" .)) }} + - name: REDIS_EXPORTER_TLS_CA_CERT_FILE + value: {{ template "redis.tlsCACert" . }} + {{- end }} + {{- end }} + {{- if .Values.metrics.extraEnvVars }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.extraEnvVars "context" $) | nindent 12 }} + {{- end }} + ports: + - name: metrics + containerPort: {{ .Values.metrics.containerPorts.http }} + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.metrics.customStartupProbe }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.customStartupProbe "context" $) | nindent 12 }} + {{- else if .Values.metrics.startupProbe.enabled }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.metrics.startupProbe "enabled") "context" $) | nindent 12 }} + tcpSocket: + port: metrics + {{- end }} + {{- if .Values.metrics.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.customLivenessProbe "context" $) | nindent 12 }} + {{- else if .Values.metrics.livenessProbe.enabled }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.metrics.livenessProbe "enabled") "context" $) | nindent 12 }} + tcpSocket: + port: metrics + {{- end }} + {{- if .Values.metrics.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.customReadinessProbe "context" $) | nindent 12 }} + {{- else if .Values.metrics.readinessProbe.enabled }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.metrics.readinessProbe "enabled") "context" $) | nindent 12 }} + httpGet: + path: / + port: metrics + {{- end }} + {{- end }} + {{- if .Values.metrics.resources }} + resources: {{- toYaml .Values.metrics.resources | nindent 12 }} + {{- else if ne .Values.metrics.resourcesPreset "none" }} + resources: {{- include "common.resources.preset" (dict "type" .Values.metrics.resourcesPreset) | nindent 12 }} + {{- end }} + volumeMounts: + - name: empty-dir + mountPath: /tmp + subPath: app-tmp-dir + {{- if .Values.auth.usePasswordFiles }} + - name: redis-password + mountPath: /secrets/ + {{- end }} + {{- if .Values.tls.enabled }} + - name: redis-certificates + mountPath: /opt/bitnami/redis/certs + readOnly: true + {{- end }} + {{- if .Values.metrics.extraVolumeMounts }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.extraVolumeMounts "context" $ ) | nindent 12 }} + {{- end }} + {{- end }} + {{- if .Values.master.sidecars }} + {{- include "common.tplvalues.render" (dict "value" .Values.master.sidecars "context" $) | nindent 8 }} + {{- end }} + {{- $needsVolumePermissions := and .Values.volumePermissions.enabled .Values.master.persistence.enabled .Values.master.podSecurityContext.enabled .Values.master.containerSecurityContext.enabled }} + {{- if or .Values.master.initContainers $needsVolumePermissions .Values.sysctl.enabled }} + initContainers: + {{- if .Values.master.initContainers }} + {{- include "common.tplvalues.render" (dict "value" .Values.master.initContainers "context" $) | nindent 8 }} + {{- end }} + {{- if $needsVolumePermissions }} + - name: volume-permissions + image: {{ include "redis.volumePermissions.image" . }} + imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | quote }} + command: + - /bin/bash + - -ec + - | + {{- if eq ( toString ( .Values.volumePermissions.containerSecurityContext.runAsUser )) "auto" }} + chown -R `id -u`:`id -G | cut -d " " -f2` {{ .Values.master.persistence.path }} + {{- else }} + chown -R {{ .Values.master.containerSecurityContext.runAsUser }}:{{ .Values.master.podSecurityContext.fsGroup }} {{ .Values.master.persistence.path }} + {{- end }} + {{- if eq ( toString ( .Values.volumePermissions.containerSecurityContext.runAsUser )) "auto" }} + securityContext: {{- omit .Values.volumePermissions.containerSecurityContext "runAsUser" | toYaml | nindent 12 }} + {{- else }} + securityContext: {{- .Values.volumePermissions.containerSecurityContext | toYaml | nindent 12 }} + {{- end }} + {{- if .Values.volumePermissions.extraEnvVars }} + env: + {{- include "common.tplvalues.render" (dict "value" .Values.volumePermissions.extraEnvVars "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.volumePermissions.resources }} + resources: {{- toYaml .Values.volumePermissions.resources | nindent 12 }} + {{- else if ne .Values.volumePermissions.resourcesPreset "none" }} + resources: {{- include "common.resources.preset" (dict "type" .Values.volumePermissions.resourcesPreset) | nindent 12 }} + {{- end }} + volumeMounts: + - name: empty-dir + mountPath: /tmp + subPath: tmp-dir + - name: redis-data + mountPath: {{ .Values.master.persistence.path }} + {{- if .Values.master.persistence.subPath }} + subPath: {{ .Values.master.persistence.subPath }} + {{- else if .Values.master.persistence.subPathExpr }} + subPathExpr: {{ .Values.master.persistence.subPathExpr }} + {{- end }} + {{- end }} + {{- if .Values.sysctl.enabled }} + - name: init-sysctl + image: {{ include "redis.sysctl.image" . }} + imagePullPolicy: {{ default "" .Values.sysctl.image.pullPolicy | quote }} + securityContext: + privileged: true + runAsUser: 0 + {{- if .Values.sysctl.command }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.sysctl.command "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.sysctl.resources }} + resources: {{- toYaml .Values.sysctl.resources | nindent 12 }} + {{- else if ne .Values.sysctl.resourcesPreset "none" }} + resources: {{- include "common.resources.preset" (dict "type" .Values.sysctl.resourcesPreset) | nindent 12 }} + {{- end }} + {{- if .Values.sysctl.mountHostSys }} + volumeMounts: + - name: empty-dir + mountPath: /tmp + subPath: tmp-dir + - name: host-sys + mountPath: /host-sys + {{- end }} + {{- end }} + {{- end }} + volumes: + - name: start-scripts + configMap: + name: {{ printf "%s-scripts" (include "common.names.fullname" .) }} + defaultMode: 0755 + - name: health + configMap: + name: {{ printf "%s-health" (include "common.names.fullname" .) }} + defaultMode: 0755 + {{- if .Values.auth.usePasswordFiles }} + - name: redis-password + {{ if .Values.auth.usePasswordFileFromSecret }} + secret: + secretName: {{ template "redis.secretName" . }} + items: + - key: {{ template "redis.secretPasswordKey" . }} + path: redis-password + {{- else }} + emptyDir: {} + {{- end }} + {{- end }} + - name: config + configMap: + name: {{ include "redis.configmapName" . }} + {{- if .Values.sysctl.mountHostSys }} + - name: host-sys + hostPath: + path: /sys + {{- end }} + - name: empty-dir + {{- if or .Values.master.persistence.medium .Values.master.persistence.sizeLimit }} + emptyDir: + {{- if .Values.master.persistence.medium }} + medium: {{ .Values.master.persistence.medium | quote }} + {{- end }} + {{- if .Values.master.persistence.sizeLimit }} + sizeLimit: {{ .Values.master.persistence.sizeLimit | quote }} + {{- end }} + {{- else }} + emptyDir: {} + {{- end }} + {{- if .Values.tls.enabled }} + - name: redis-certificates + secret: + secretName: {{ include "redis.tlsSecretName" . }} + defaultMode: 256 + {{- end }} + {{- if .Values.master.extraVolumes }} + {{- include "common.tplvalues.render" ( dict "value" .Values.master.extraVolumes "context" $ ) | nindent 8 }} + {{- end }} + {{- if .Values.metrics.extraVolumes }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.extraVolumes "context" $ ) | nindent 8 }} + {{- end }} + {{- if or (not .Values.master.persistence.enabled) (eq .Values.master.kind "DaemonSet") }} + - name: redis-data + {{- if or .Values.master.persistence.medium .Values.master.persistence.sizeLimit }} + emptyDir: + {{- if .Values.master.persistence.medium }} + medium: {{ .Values.master.persistence.medium | quote }} + {{- end }} + {{- if .Values.master.persistence.sizeLimit }} + sizeLimit: {{ .Values.master.persistence.sizeLimit | quote }} + {{- end }} + {{- else }} + emptyDir: {} + {{- end }} + {{- else if .Values.master.persistence.existingClaim }} + - name: redis-data + persistentVolumeClaim: + claimName: {{ printf "%s" (tpl .Values.master.persistence.existingClaim .) }} + {{- else if (eq .Values.master.kind "Deployment") }} + - name: redis-data + persistentVolumeClaim: + claimName: {{ printf "redis-data-%s-master" (include "common.names.fullname" .) }} + {{- else }} + {{- if .Values.master.persistentVolumeClaimRetentionPolicy.enabled }} + persistentVolumeClaimRetentionPolicy: + whenDeleted: {{ .Values.master.persistentVolumeClaimRetentionPolicy.whenDeleted }} + whenScaled: {{ .Values.master.persistentVolumeClaimRetentionPolicy.whenScaled }} + {{- end }} + volumeClaimTemplates: + - apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + name: redis-data + {{- $claimLabels := include "common.tplvalues.merge" ( dict "values" ( list .Values.master.persistence.labels .Values.commonLabels ) "context" . ) }} + labels: {{- include "common.labels.matchLabels" ( dict "customLabels" $claimLabels "context" $ ) | nindent 10 }} + app.kubernetes.io/component: master + {{- if .Values.master.persistence.annotations }} + annotations: {{- toYaml .Values.master.persistence.annotations | nindent 10 }} + {{- end }} + spec: + accessModes: + {{- range .Values.master.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.master.persistence.size | quote }} + {{- if .Values.master.persistence.selector }} + selector: {{- include "common.tplvalues.render" (dict "value" .Values.master.persistence.selector "context" $) | nindent 10 }} + {{- end }} + {{- if .Values.master.persistence.dataSource }} + dataSource: {{- include "common.tplvalues.render" (dict "value" .Values.master.persistence.dataSource "context" $) | nindent 10 }} + {{- end }} + {{- include "common.storage.class" (dict "persistence" .Values.master.persistence "global" .Values.global) | nindent 8 }} + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/master/pdb.yaml b/charts/open-webui/charts/redis/templates/master/pdb.yaml new file mode 100644 index 0000000..dab636d --- /dev/null +++ b/charts/open-webui/charts/redis/templates/master/pdb.yaml @@ -0,0 +1,27 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} +{{- $pdb := coalesce .Values.pdb .Values.master.pdb }} +{{- if and $pdb.create (gt (int64 .Values.master.count) 0) (or (not (eq .Values.architecture "replication")) (not .Values.sentinel.enabled)) }} +apiVersion: {{ include "common.capabilities.policy.apiVersion" . }} +kind: PodDisruptionBudget +metadata: + name: {{ printf "%s-master" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: master + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + {{- if $pdb.minAvailable }} + minAvailable: {{ $pdb.minAvailable }} + {{- end }} + {{- if or $pdb.maxUnavailable (not $pdb.minAvailable)}} + maxUnavailable: {{ $pdb.maxUnavailable | default 1 }} + {{- end }} + selector: + matchLabels: {{- include "common.labels.matchLabels" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 6 }} + app.kubernetes.io/component: master +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/master/psp.yaml b/charts/open-webui/charts/redis/templates/master/psp.yaml new file mode 100644 index 0000000..2a685f8 --- /dev/null +++ b/charts/open-webui/charts/redis/templates/master/psp.yaml @@ -0,0 +1,47 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if and (include "common.capabilities.psp.supported" .) .Values.podSecurityPolicy.create }} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ printf "%s-master" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + allowPrivilegeEscalation: false + fsGroup: + rule: 'MustRunAs' + ranges: + - min: {{ .Values.master.podSecurityContext.fsGroup }} + max: {{ .Values.master.podSecurityContext.fsGroup }} + hostIPC: false + hostNetwork: false + hostPID: false + privileged: false + readOnlyRootFilesystem: false + requiredDropCapabilities: + - ALL + runAsUser: + rule: 'MustRunAs' + ranges: + - min: {{ .Values.master.containerSecurityContext.runAsUser }} + max: {{ .Values.master.containerSecurityContext.runAsUser }} + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'MustRunAs' + ranges: + - min: {{ .Values.master.containerSecurityContext.runAsUser }} + max: {{ .Values.master.containerSecurityContext.runAsUser }} + volumes: + - 'configMap' + - 'secret' + - 'emptyDir' + - 'persistentVolumeClaim' +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/master/pvc.yaml b/charts/open-webui/charts/redis/templates/master/pvc.yaml new file mode 100644 index 0000000..13aee50 --- /dev/null +++ b/charts/open-webui/charts/redis/templates/master/pvc.yaml @@ -0,0 +1,33 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if and (eq .Values.architecture "standalone") (eq .Values.master.kind "Deployment") (.Values.master.persistence.enabled) (not .Values.master.persistence.existingClaim) }} +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: {{ printf "redis-data-%s-master" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + {{- $labels := include "common.tplvalues.merge" ( dict "values" ( list .Values.master.persistence.labels .Values.commonLabels ) "context" . ) }} + labels: {{- include "common.labels.standard" ( dict "customLabels" $labels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: master + {{- if .Values.master.persistence.annotations }} + annotations: {{- toYaml .Values.master.persistence.annotations | nindent 4 }} + {{- end }} +spec: + accessModes: + {{- range .Values.master.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.master.persistence.size | quote }} + {{- if .Values.master.persistence.selector }} + selector: {{- include "common.tplvalues.render" (dict "value" .Values.master.persistence.selector "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.master.persistence.dataSource }} + dataSource: {{- include "common.tplvalues.render" (dict "value" .Values.master.persistence.dataSource "context" $) | nindent 4 }} + {{- end }} + {{- include "common.storage.class" (dict "persistence" .Values.master.persistence "global" .Values.global) | nindent 2 }} +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/master/service.yaml b/charts/open-webui/charts/redis/templates/master/service.yaml new file mode 100644 index 0000000..b9bf47d --- /dev/null +++ b/charts/open-webui/charts/redis/templates/master/service.yaml @@ -0,0 +1,62 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if and (not .Values.sentinel.enabled) (gt (int64 .Values.master.count) 0) }} +apiVersion: v1 +kind: Service +metadata: + name: {{ printf "%s-master" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: master + {{- if or .Values.master.service.annotations .Values.commonAnnotations }} + {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list .Values.master.service.annotations .Values.commonAnnotations ) "context" . ) }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.master.service.type }} + {{- if or (eq .Values.master.service.type "LoadBalancer") (eq .Values.master.service.type "NodePort") }} + externalTrafficPolicy: {{ .Values.master.service.externalTrafficPolicy | quote }} + {{- end }} + {{- if (semverCompare ">=1.22-0" (include "common.capabilities.kubeVersion" .)) }} + internalTrafficPolicy: {{ .Values.master.service.internalTrafficPolicy }} + {{- end }} + {{- if and (eq .Values.master.service.type "LoadBalancer") (not (empty .Values.master.service.loadBalancerIP)) }} + loadBalancerIP: {{ .Values.master.service.loadBalancerIP }} + {{- end }} + {{- if and (eq .Values.master.service.type "LoadBalancer") .Values.master.service.loadBalancerClass }} + loadBalancerClass: {{ .Values.master.service.loadBalancerClass }} + {{- end }} + {{- if and (eq .Values.master.service.type "LoadBalancer") (not (empty .Values.master.service.loadBalancerSourceRanges)) }} + loadBalancerSourceRanges: {{ toYaml .Values.master.service.loadBalancerSourceRanges | nindent 4 }} + {{- end }} + {{- if and .Values.master.service.clusterIP (eq .Values.master.service.type "ClusterIP") }} + clusterIP: {{ .Values.master.service.clusterIP }} + {{- end }} + {{- if .Values.master.service.sessionAffinity }} + sessionAffinity: {{ .Values.master.service.sessionAffinity }} + {{- end }} + {{- if .Values.master.service.sessionAffinityConfig }} + sessionAffinityConfig: {{- include "common.tplvalues.render" (dict "value" .Values.master.service.sessionAffinityConfig "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.master.service.externalIPs }} + externalIPs: {{- include "common.tplvalues.render" (dict "value" .Values.master.service.externalIPs "context" $) | nindent 4 }} + {{- end }} + ports: + - name: {{ .Values.master.service.portNames.redis }} + port: {{ .Values.master.service.ports.redis }} + targetPort: redis + {{- if and (or (eq .Values.master.service.type "NodePort") (eq .Values.master.service.type "LoadBalancer")) .Values.master.service.nodePorts.redis}} + nodePort: {{ .Values.master.service.nodePorts.redis}} + {{- else if eq .Values.master.service.type "ClusterIP" }} + nodePort: null + {{- end }} + {{- if .Values.master.service.extraPorts }} + {{- include "common.tplvalues.render" (dict "value" .Values.master.service.extraPorts "context" $) | nindent 4 }} + {{- end }} + {{- $podLabels := include "common.tplvalues.merge" ( dict "values" ( list .Values.master.podLabels .Values.commonLabels ) "context" . ) }} + selector: {{- include "common.labels.matchLabels" ( dict "customLabels" $podLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: master +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/master/serviceaccount.yaml b/charts/open-webui/charts/redis/templates/master/serviceaccount.yaml new file mode 100644 index 0000000..bf58cc5 --- /dev/null +++ b/charts/open-webui/charts/redis/templates/master/serviceaccount.yaml @@ -0,0 +1,18 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if and .Values.master.serviceAccount.create (or (not (eq .Values.architecture "replication")) (not .Values.sentinel.enabled)) }} +apiVersion: v1 +kind: ServiceAccount +automountServiceAccountToken: {{ .Values.master.serviceAccount.automountServiceAccountToken }} +metadata: + name: {{ template "redis.masterServiceAccountName" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if or .Values.master.serviceAccount.annotations .Values.commonAnnotations }} + {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list .Values.master.serviceAccount.annotations .Values.commonAnnotations ) "context" . ) }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/metrics-svc.yaml b/charts/open-webui/charts/redis/templates/metrics-svc.yaml new file mode 100644 index 0000000..529122e --- /dev/null +++ b/charts/open-webui/charts/redis/templates/metrics-svc.yaml @@ -0,0 +1,44 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if and .Values.metrics.enabled .Values.metrics.service.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ printf "%s-metrics" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: metrics + {{- if or .Values.metrics.service.annotations .Values.commonAnnotations }} + {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list .Values.metrics.service.annotations .Values.commonAnnotations ) "context" . ) }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.metrics.service.type }} + {{- if and .Values.metrics.service.clusterIP (eq .Values.metrics.service.type "ClusterIP") }} + clusterIP: {{ .Values.metrics.service.clusterIP }} + {{- end }} + {{- if eq .Values.metrics.service.type "LoadBalancer" }} + externalTrafficPolicy: {{ .Values.metrics.service.externalTrafficPolicy }} + {{- end }} + {{- if and (eq .Values.metrics.service.type "LoadBalancer") .Values.metrics.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.metrics.service.loadBalancerIP }} + {{- end }} + {{- if and (eq .Values.metrics.service.type "LoadBalancer") .Values.metrics.service.loadBalancerClass }} + loadBalancerClass: {{ .Values.metrics.service.loadBalancerClass }} + {{- end }} + {{- if and (eq .Values.metrics.service.type "LoadBalancer") .Values.metrics.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: {{- toYaml .Values.metrics.service.loadBalancerSourceRanges | nindent 4 }} + {{- end }} + ports: + - name: http-metrics + port: {{ coalesce .Values.metrics.service.ports.http .Values.metrics.service.port }} + protocol: TCP + targetPort: metrics + {{- if .Values.metrics.service.extraPorts }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.service.extraPorts "context" $) | nindent 4 }} + {{- end }} + selector: {{- include "common.labels.matchLabels" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/networkpolicy.yaml b/charts/open-webui/charts/redis/templates/networkpolicy.yaml new file mode 100644 index 0000000..3d652c6 --- /dev/null +++ b/charts/open-webui/charts/redis/templates/networkpolicy.yaml @@ -0,0 +1,108 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if .Values.networkPolicy.enabled }} +kind: NetworkPolicy +apiVersion: {{ template "networkPolicy.apiVersion" . }} +metadata: + name: {{ template "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + podSelector: + matchLabels: {{- include "common.labels.matchLabels" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 6 }} + policyTypes: + - Ingress + - Egress + {{- if .Values.networkPolicy.allowExternalEgress }} + egress: + - {} + {{- else }} + egress: + {{- if eq .Values.architecture "replication" }} + # Allow dns resolution + - ports: + - port: 53 + protocol: UDP + # Allow outbound connections to other cluster pods + - ports: + - port: {{ .Values.master.containerPorts.redis }} + {{- if .Values.sentinel.enabled }} + - port: {{ .Values.sentinel.containerPorts.sentinel }} + {{- end }} + to: + - podSelector: + matchLabels: {{- include "common.labels.matchLabels" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 14 }} + {{- end }} + {{- if .Values.networkPolicy.extraEgress }} + {{- include "common.tplvalues.render" ( dict "value" .Values.networkPolicy.extraEgress "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} + ingress: + # Allow inbound connections + - ports: + - port: {{ .Values.master.containerPorts.redis }} + {{- if .Values.sentinel.enabled }} + - port: {{ .Values.sentinel.containerPorts.sentinel }} + {{- end }} + {{- if not .Values.networkPolicy.allowExternal }} + from: + - podSelector: + matchLabels: + {{ template "common.names.fullname" . }}-client: "true" + - podSelector: + matchLabels: {{- include "common.labels.matchLabels" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 14 }} + {{- if or .Values.networkPolicy.ingressNSMatchLabels .Values.networkPolicy.ingressNSPodMatchLabels }} + - namespaceSelector: + matchLabels: + {{- if .Values.networkPolicy.ingressNSMatchLabels }} + {{- range $key, $value := .Values.networkPolicy.ingressNSMatchLabels }} + {{ $key | quote }}: {{ $value | quote }} + {{- end }} + {{ else }} + {} + {{- end }} + {{- if .Values.networkPolicy.ingressNSPodMatchLabels }} + podSelector: + matchLabels: + {{- range $key, $value := .Values.networkPolicy.ingressNSPodMatchLabels }} + {{ $key | quote }}: {{ $value | quote }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.metrics.enabled }} + # Allow prometheus scrapes for metrics + - ports: + - port: {{ .Values.metrics.containerPorts.http }} + {{- if not .Values.networkPolicy.metrics.allowExternal }} + from: + {{- if or .Values.networkPolicy.metrics.ingressNSMatchLabels .Values.networkPolicy.metrics.ingressNSPodMatchLabels }} + - namespaceSelector: + matchLabels: + {{- if .Values.networkPolicy.metrics.ingressNSMatchLabels }} + {{- range $key, $value := .Values.networkPolicy.metrics.ingressNSMatchLabels }} + {{ $key | quote }}: {{ $value | quote }} + {{- end }} + {{ else }} + {} + {{- end }} + {{- if .Values.networkPolicy.metrics.ingressNSPodMatchLabels }} + podSelector: + matchLabels: + {{- range $key, $value := .Values.networkPolicy.metrics.ingressNSPodMatchLabels }} + {{ $key | quote }}: {{ $value | quote }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.networkPolicy.extraIngress }} + {{- include "common.tplvalues.render" ( dict "value" .Values.networkPolicy.extraIngress "context" $ ) | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/podmonitor.yaml b/charts/open-webui/charts/redis/templates/podmonitor.yaml new file mode 100644 index 0000000..3297610 --- /dev/null +++ b/charts/open-webui/charts/redis/templates/podmonitor.yaml @@ -0,0 +1,81 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if and .Values.metrics.enabled .Values.metrics.podMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: PodMonitor +metadata: + name: {{ template "common.names.fullname" . }} + namespace: {{ default (include "common.names.namespace" .) .Values.metrics.podMonitor.namespace | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if .Values.metrics.podMonitor.additionalLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.podMonitor.additionalLabels "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + podMetricsEndpoints: + - port: {{ .Values.metrics.podMonitor.port }} + {{- if .Values.metrics.podMonitor.interval }} + interval: {{ .Values.metrics.podMonitor.interval }} + {{- end }} + {{- if .Values.metrics.podMonitor.scrapeTimeout }} + scrapeTimeout: {{ .Values.metrics.podMonitor.scrapeTimeout }} + {{- end }} + {{- if .Values.metrics.podMonitor.honorLabels }} + honorLabels: {{ .Values.metrics.podMonitor.honorLabels }} + {{- end }} + {{- with concat .Values.metrics.podMonitor.relabelings .Values.metrics.podMonitor.relabellings }} + relabelings: {{- toYaml . | nindent 6 }} + {{- end }} + {{- if .Values.metrics.podMonitor.metricRelabelings }} + metricRelabelings: {{- toYaml .Values.metrics.podMonitor.metricRelabelings | nindent 6 }} + {{- end }} + {{- range .Values.metrics.podMonitor.additionalEndpoints }} + - port: {{ .port }} + {{- if .interval }} + interval: {{ .interval }} + {{- end }} + {{- if .path }} + path: {{ .path }} + {{- end }} + {{- if .honorLabels }} + honorLabels: {{ .honorLabels }} + {{- end }} + {{- with concat .relabelings .relabellings }} + relabelings: {{- toYaml . | nindent 6 }} + {{- end }} + {{- if .metricRelabelings }} + metricRelabelings: {{- toYaml .metricRelabelings | nindent 6 }} + {{- end }} + {{- if .scrapeTimeout }} + scrapeTimeout: {{ .scrapeTimeout }} + {{- end }} + {{- if .params }} + params: + {{- range $key, $value := .params }} + {{ $key }}: + {{- range $value }} + - {{ . | quote }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.podTargetLabels }} + podTargetLabels: {{- toYaml .Values.metrics.podMonitor.podTargetLabels | nindent 4 }} + {{- end }} + {{- with .Values.metrics.podMonitor.sampleLimit -}} + sampleLimit: {{ . }} + {{- end }} + {{- with .Values.metrics.podMonitor.targetLimit -}} + targetLimit: {{ . }} + {{- end }} + namespaceSelector: + matchNames: + - {{ include "common.names.namespace" . | quote }} + selector: + matchLabels: {{- include "common.labels.matchLabels" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 6 }} +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/prometheusrule.yaml b/charts/open-webui/charts/redis/templates/prometheusrule.yaml new file mode 100644 index 0000000..56c013b --- /dev/null +++ b/charts/open-webui/charts/redis/templates/prometheusrule.yaml @@ -0,0 +1,23 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if and .Values.metrics.enabled .Values.metrics.prometheusRule.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: {{ template "common.names.fullname" . }} + namespace: {{ default (include "common.names.namespace" .) .Values.metrics.prometheusRule.namespace | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if .Values.metrics.prometheusRule.additionalLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.prometheusRule.additionalLabels "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + groups: + - name: {{ include "common.names.fullname" . }} + rules: {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.prometheusRule.rules "context" $ ) | nindent 8 }} +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/replicas/application.yaml b/charts/open-webui/charts/redis/templates/replicas/application.yaml new file mode 100644 index 0000000..66527e1 --- /dev/null +++ b/charts/open-webui/charts/redis/templates/replicas/application.yaml @@ -0,0 +1,573 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if and (eq .Values.architecture "replication") (not .Values.sentinel.enabled) }} +apiVersion: {{ include "common.capabilities.statefulset.apiVersion" . }} +kind: {{ .Values.replica.kind }} +metadata: + name: {{ printf "%s-replicas" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: replica + {{- if or .Values.commonAnnotations .Values.replica.annotations }} + {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list .Values.replica.annotations .Values.commonAnnotations ) "context" . ) }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} + {{- end }} +spec: + {{- if and (not (eq .Values.replica.kind "DaemonSet")) (not .Values.replica.autoscaling.enabled) }} + replicas: {{ .Values.replica.replicaCount }} + {{- end }} + revisionHistoryLimit: {{ .Values.replica.revisionHistoryLimit }} + {{- $podLabels := include "common.tplvalues.merge" ( dict "values" ( list .Values.replica.podLabels .Values.commonLabels ) "context" . ) }} + selector: + matchLabels: {{- include "common.labels.matchLabels" ( dict "customLabels" $podLabels "context" $ ) | nindent 6 }} + app.kubernetes.io/component: replica + {{- if (eq .Values.replica.kind "StatefulSet") }} + serviceName: {{ printf "%s-headless" (include "common.names.fullname" .) }} + {{- end }} + {{- if .Values.replica.updateStrategy }} + updateStrategy: {{- toYaml .Values.replica.updateStrategy | nindent 4 }} + {{- end }} + {{- if and .Values.replica.minReadySeconds (semverCompare ">= 1.23-0" (include "common.capabilities.kubeVersion" .)) }} + minReadySeconds: {{ .Values.replica.minReadySeconds }} + {{- end }} + {{- if .Values.replica.podManagementPolicy }} + podManagementPolicy: {{ .Values.replica.podManagementPolicy | quote }} + {{- end }} + template: + metadata: + labels: {{- include "common.labels.standard" ( dict "customLabels" $podLabels "context" $ ) | nindent 8 }} + app.kubernetes.io/component: replica + {{- if and .Values.metrics.enabled .Values.metrics.podLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.podLabels "context" $ ) | nindent 8 }} + {{- end }} + annotations: + {{- if (include "redis.createConfigmap" .) }} + checksum/configmap: {{ pick ( include (print $.Template.BasePath "/configmap.yaml") . | fromYaml ) "data" | toYaml | sha256sum }} + {{- end }} + checksum/health: {{ pick ( include (print $.Template.BasePath "/health-configmap.yaml") . | fromYaml ) "data" | toYaml | sha256sum }} + checksum/scripts: {{ pick ( include (print $.Template.BasePath "/scripts-configmap.yaml") . | fromYaml ) "data" | toYaml | sha256sum }} + checksum/secret: {{ pick ( include (print $.Template.BasePath "/secret.yaml") . | fromYaml ) "data" | toYaml | sha256sum }} + {{- if .Values.replica.podAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.replica.podAnnotations "context" $ ) | nindent 8 }} + {{- end }} + {{- if and .Values.metrics.enabled .Values.metrics.podAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.podAnnotations "context" $ ) | nindent 8 }} + {{- end }} + spec: + {{- if .Values.replica.extraPodSpec }} + {{- include "common.tplvalues.render" (dict "value" .Values.replica.extraPodSpec "context" $) | nindent 6 }} + {{- end }} + {{- include "redis.imagePullSecrets" . | nindent 6 }} + {{- if .Values.replica.hostAliases }} + hostAliases: {{- include "common.tplvalues.render" (dict "value" .Values.replica.hostAliases "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.replica.podSecurityContext.enabled }} + securityContext: {{- include "common.compatibility.renderSecurityContext" (dict "secContext" .Values.replica.podSecurityContext "context" $) | nindent 8 }} + {{- end }} + serviceAccountName: {{ template "redis.replicaServiceAccountName" . }} + automountServiceAccountToken: {{ .Values.replica.automountServiceAccountToken }} + {{- if .Values.replica.priorityClassName }} + priorityClassName: {{ .Values.replica.priorityClassName | quote }} + {{- end }} + {{- if .Values.replica.affinity }} + affinity: {{- include "common.tplvalues.render" (dict "value" .Values.replica.affinity "context" $) | nindent 8 }} + {{- else }} + affinity: + podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.replica.podAffinityPreset "component" "replica" "customLabels" $podLabels "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.replica.podAntiAffinityPreset "component" "replica" "customLabels" $podLabels "context" $) | nindent 10 }} + nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.replica.nodeAffinityPreset.type "key" .Values.replica.nodeAffinityPreset.key "values" .Values.replica.nodeAffinityPreset.values) | nindent 10 }} + {{- end }} + {{- if .Values.replica.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.replica.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.replica.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.replica.tolerations "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.replica.topologySpreadConstraints }} + topologySpreadConstraints: {{- include "common.tplvalues.render" (dict "value" .Values.replica.topologySpreadConstraints "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.replica.shareProcessNamespace }} + shareProcessNamespace: {{ .Values.replica.shareProcessNamespace }} + {{- end }} + {{- if .Values.replica.schedulerName }} + schedulerName: {{ .Values.replica.schedulerName | quote }} + {{- end }} + {{- if .Values.replica.dnsPolicy }} + dnsPolicy: {{ .Values.replica.dnsPolicy }} + {{- end }} + {{- if .Values.replica.dnsConfig }} + dnsConfig: {{- include "common.tplvalues.render" (dict "value" .Values.replica.dnsConfig "context" $) | nindent 8 }} + {{- end }} + enableServiceLinks: {{ .Values.replica.enableServiceLinks }} + terminationGracePeriodSeconds: {{ .Values.replica.terminationGracePeriodSeconds }} + containers: + - name: redis + image: {{ template "redis.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.replica.lifecycleHooks }} + lifecycle: {{- include "common.tplvalues.render" (dict "value" .Values.replica.lifecycleHooks "context" $) | nindent 12 }} + {{- end }} + {{- end }} + {{- if .Values.replica.containerSecurityContext.enabled }} + securityContext: {{- include "common.compatibility.renderSecurityContext" (dict "secContext" .Values.replica.containerSecurityContext "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} + {{- else if .Values.replica.command }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.replica.command "context" $) | nindent 12 }} + {{- else }} + command: + - /bin/bash + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} + {{- else if .Values.replica.args }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.replica.args "context" $) | nindent 12 }} + {{- else }} + args: + - -c + - /opt/bitnami/scripts/start-scripts/start-replica.sh + {{- end }} + env: + - name: BITNAMI_DEBUG + value: {{ ternary "true" "false" (or .Values.image.debug .Values.diagnosticMode.enabled) | quote }} + - name: REDIS_REPLICATION_MODE + value: replica + - name: REDIS_MASTER_HOST + {{- if .Values.replica.externalMaster.enabled }} + value: {{ .Values.replica.externalMaster.host | quote }} + {{- else if and (eq (int64 .Values.master.count) 1) (eq .Values.master.kind "StatefulSet") }} + value: {{ template "common.names.fullname" . }}-master-0.{{ template "common.names.fullname" . }}-headless.{{ include "common.names.namespace" . }}.svc.{{ .Values.clusterDomain }} + {{- else }} + value: {{ template "common.names.fullname" . }}-master.{{ include "common.names.namespace" . }}.svc.{{ .Values.clusterDomain }} + {{- end }} + - name: REDIS_MASTER_PORT_NUMBER + {{- if .Values.replica.externalMaster.enabled }} + value: {{ .Values.replica.externalMaster.port | quote }} + {{- else }} + value: {{ .Values.master.containerPorts.redis | quote }} + {{- end }} + - name: ALLOW_EMPTY_PASSWORD + value: {{ ternary "no" "yes" .Values.auth.enabled | quote }} + {{- if .Values.auth.enabled }} + {{- if .Values.auth.usePasswordFiles }} + - name: REDIS_PASSWORD_FILE + value: "/opt/bitnami/redis/secrets/redis-password" + - name: REDIS_MASTER_PASSWORD_FILE + value: "/opt/bitnami/redis/secrets/redis-password" + {{- else }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "redis.secretName" . }} + key: {{ template "redis.secretPasswordKey" . }} + - name: REDIS_MASTER_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "redis.secretName" . }} + key: {{ template "redis.secretPasswordKey" . }} + {{- end }} + {{- end }} + - name: REDIS_TLS_ENABLED + value: {{ ternary "yes" "no" .Values.tls.enabled | quote }} + {{- if .Values.tls.enabled }} + - name: REDIS_TLS_PORT + value: {{ .Values.replica.containerPorts.redis | quote }} + - name: REDIS_TLS_AUTH_CLIENTS + value: {{ ternary "yes" "no" .Values.tls.authClients | quote }} + - name: REDIS_TLS_CERT_FILE + value: {{ template "redis.tlsCert" . }} + - name: REDIS_TLS_KEY_FILE + value: {{ template "redis.tlsCertKey" . }} + {{- if not (empty (include "redis.tlsCACert" .)) }} + - name: REDIS_TLS_CA_FILE + value: {{ template "redis.tlsCACert" . }} + {{- end }} + {{- if .Values.tls.dhParamsFilename }} + - name: REDIS_TLS_DH_PARAMS_FILE + value: {{ template "redis.tlsDHParams" . }} + {{- end }} + {{- else }} + - name: REDIS_PORT + value: {{ .Values.replica.containerPorts.redis | quote }} + {{- end }} + {{- if .Values.replica.extraEnvVars }} + {{- include "common.tplvalues.render" (dict "value" .Values.replica.extraEnvVars "context" $) | nindent 12 }} + {{- end }} + {{- if or .Values.replica.extraEnvVarsCM .Values.replica.extraEnvVarsSecret }} + envFrom: + {{- if .Values.replica.extraEnvVarsCM }} + - configMapRef: + name: {{ .Values.replica.extraEnvVarsCM }} + {{- end }} + {{- if .Values.replica.extraEnvVarsSecret }} + - secretRef: + name: {{ .Values.replica.extraEnvVarsSecret }} + {{- end }} + {{- end }} + ports: + - name: redis + containerPort: {{ .Values.replica.containerPorts.redis }} + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.replica.customStartupProbe }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.replica.customStartupProbe "context" $) | nindent 12 }} + {{- else if .Values.replica.startupProbe.enabled }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.replica.startupProbe "enabled") "context" $) | nindent 12 }} + tcpSocket: + port: redis + {{- end }} + {{- if .Values.replica.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.replica.customLivenessProbe "context" $) | nindent 12 }} + {{- else if .Values.replica.livenessProbe.enabled }} + livenessProbe: + initialDelaySeconds: {{ .Values.replica.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.replica.livenessProbe.periodSeconds }} + timeoutSeconds: {{ add1 .Values.replica.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.replica.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.replica.livenessProbe.failureThreshold }} + exec: + command: + - sh + - -c + - /health/ping_liveness_local_and_master.sh {{ .Values.replica.livenessProbe.timeoutSeconds }} + {{- end }} + {{- if .Values.replica.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.replica.customReadinessProbe "context" $) | nindent 12 }} + {{- else if .Values.replica.readinessProbe.enabled }} + readinessProbe: + initialDelaySeconds: {{ .Values.replica.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.replica.readinessProbe.periodSeconds }} + timeoutSeconds: {{ add1 .Values.replica.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.replica.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.replica.readinessProbe.failureThreshold }} + exec: + command: + - sh + - -c + - /health/ping_readiness_local_and_master.sh {{ .Values.replica.readinessProbe.timeoutSeconds }} + {{- end }} + {{- end }} + {{- if .Values.replica.resources }} + resources: {{- toYaml .Values.replica.resources | nindent 12 }} + {{- else if ne .Values.replica.resourcesPreset "none" }} + resources: {{- include "common.resources.preset" (dict "type" .Values.replica.resourcesPreset) | nindent 12 }} + {{- end }} + volumeMounts: + - name: start-scripts + mountPath: /opt/bitnami/scripts/start-scripts + - name: health + mountPath: /health + {{- if .Values.auth.usePasswordFiles }} + - name: redis-password + mountPath: /opt/bitnami/redis/secrets/ + {{- end }} + - name: redis-data + mountPath: /data + {{- if .Values.replica.persistence.subPath }} + subPath: {{ .Values.replica.persistence.subPath }} + {{- else if .Values.replica.persistence.subPathExpr }} + subPathExpr: {{ .Values.replica.persistence.subPathExpr }} + {{- end }} + - name: config + mountPath: /opt/bitnami/redis/mounted-etc + - name: empty-dir + mountPath: /opt/bitnami/redis/etc + subPath: app-conf-dir + - name: empty-dir + mountPath: /tmp + subPath: tmp-dir + {{- if .Values.tls.enabled }} + - name: redis-certificates + mountPath: /opt/bitnami/redis/certs + readOnly: true + {{- end }} + {{- if .Values.replica.extraVolumeMounts }} + {{- include "common.tplvalues.render" ( dict "value" .Values.replica.extraVolumeMounts "context" $ ) | nindent 12 }} + {{- end }} + {{- if .Values.metrics.enabled }} + - name: metrics + image: {{ include "redis.metrics.image" . }} + imagePullPolicy: {{ .Values.metrics.image.pullPolicy | quote }} + {{- if .Values.metrics.containerSecurityContext.enabled }} + securityContext: {{- include "common.compatibility.renderSecurityContext" (dict "secContext" .Values.metrics.containerSecurityContext "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} + {{- else if .Values.metrics.command }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.command "context" $) | nindent 12 }} + {{- else }} + command: + - /bin/bash + - -c + - | + if [[ -f '/secrets/redis-password' ]]; then + export REDIS_PASSWORD=$(cat /secrets/redis-password) + fi + redis_exporter{{- range $key, $value := .Values.metrics.extraArgs }} --{{ $key }}={{ $value }}{{- end }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} + {{- end }} + env: + - name: REDIS_ALIAS + value: {{ template "common.names.fullname" . }} + - name: REDIS_EXPORTER_WEB_LISTEN_ADDRESS + value: {{ printf ":%v" .Values.metrics.containerPorts.http }} + {{- if .Values.auth.enabled }} + - name: REDIS_USER + value: default + {{- if (not .Values.auth.usePasswordFiles) }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "redis.secretName" . }} + key: {{ template "redis.secretPasswordKey" . }} + {{- end }} + {{- end }} + {{- if .Values.tls.enabled }} + - name: REDIS_ADDR + value: rediss://{{ .Values.metrics.redisTargetHost }}:{{ .Values.replica.containerPorts.redis }} + {{- if .Values.tls.authClients }} + - name: REDIS_EXPORTER_TLS_CLIENT_KEY_FILE + value: {{ template "redis.tlsCertKey" . }} + - name: REDIS_EXPORTER_TLS_CLIENT_CERT_FILE + value: {{ template "redis.tlsCert" . }} + {{- end }} + {{- if not (empty (include "redis.tlsCACert" .)) }} + - name: REDIS_EXPORTER_TLS_CA_CERT_FILE + value: {{ template "redis.tlsCACert" . }} + {{- end }} + {{- end }} + {{- if .Values.metrics.extraEnvVars }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.extraEnvVars "context" $) | nindent 12 }} + {{- end }} + ports: + - name: metrics + containerPort: {{ .Values.metrics.containerPorts.http }} + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.metrics.customStartupProbe }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.customStartupProbe "context" $) | nindent 12 }} + {{- else if .Values.metrics.startupProbe.enabled }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.metrics.startupProbe "enabled") "context" $) | nindent 12 }} + tcpSocket: + port: metrics + {{- end }} + {{- if .Values.metrics.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.customLivenessProbe "context" $) | nindent 12 }} + {{- else if .Values.metrics.livenessProbe.enabled }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.metrics.livenessProbe "enabled") "context" $) | nindent 12 }} + tcpSocket: + port: metrics + {{- end }} + {{- if .Values.metrics.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.customReadinessProbe "context" $) | nindent 12 }} + {{- else if .Values.metrics.readinessProbe.enabled }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.metrics.readinessProbe "enabled") "context" $) | nindent 12 }} + httpGet: + path: / + port: metrics + {{- end }} + {{- end }} + {{- if .Values.metrics.resources }} + resources: {{- toYaml .Values.metrics.resources | nindent 12 }} + {{- else if ne .Values.metrics.resourcesPreset "none" }} + resources: {{- include "common.resources.preset" (dict "type" .Values.metrics.resourcesPreset) | nindent 12 }} + {{- end }} + volumeMounts: + - name: empty-dir + mountPath: /tmp + subPath: tmp-dir + {{- if .Values.auth.usePasswordFiles }} + - name: redis-password + mountPath: /secrets/ + {{- end }} + {{- if .Values.tls.enabled }} + - name: redis-certificates + mountPath: /opt/bitnami/redis/certs + readOnly: true + {{- end }} + {{- if .Values.metrics.extraVolumeMounts }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.extraVolumeMounts "context" $ ) | nindent 12 }} + {{- end }} + {{- end }} + {{- if .Values.replica.sidecars }} + {{- include "common.tplvalues.render" (dict "value" .Values.replica.sidecars "context" $) | nindent 8 }} + {{- end }} + {{- $needsVolumePermissions := and .Values.volumePermissions.enabled .Values.replica.persistence.enabled .Values.replica.podSecurityContext.enabled .Values.replica.containerSecurityContext.enabled }} + {{- if or .Values.replica.initContainers $needsVolumePermissions .Values.sysctl.enabled }} + initContainers: + {{- if .Values.replica.initContainers }} + {{- include "common.tplvalues.render" (dict "value" .Values.replica.initContainers "context" $) | nindent 8 }} + {{- end }} + {{- if $needsVolumePermissions }} + - name: volume-permissions + image: {{ include "redis.volumePermissions.image" . }} + imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | quote }} + command: + - /bin/bash + - -ec + - | + {{- if eq ( toString ( .Values.volumePermissions.containerSecurityContext.runAsUser )) "auto" }} + chown -R `id -u`:`id -G | cut -d " " -f2` {{ .Values.replica.persistence.path }} + {{- else }} + chown -R {{ .Values.replica.containerSecurityContext.runAsUser }}:{{ .Values.replica.podSecurityContext.fsGroup }} {{ .Values.replica.persistence.path }} + {{- end }} + {{- if eq ( toString ( .Values.volumePermissions.containerSecurityContext.runAsUser )) "auto" }} + securityContext: {{- omit .Values.volumePermissions.containerSecurityContext "runAsUser" | toYaml | nindent 12 }} + {{- else }} + securityContext: {{- .Values.volumePermissions.containerSecurityContext | toYaml | nindent 12 }} + {{- end }} + {{- if .Values.volumePermissions.extraEnvVars }} + env: + {{- include "common.tplvalues.render" (dict "value" .Values.volumePermissions.extraEnvVars "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.volumePermissions.resources }} + resources: {{- toYaml .Values.volumePermissions.resources | nindent 12 }} + {{- else if ne .Values.volumePermissions.resourcesPreset "none" }} + resources: {{- include "common.resources.preset" (dict "type" .Values.volumePermissions.resourcesPreset) | nindent 12 }} + {{- end }} + volumeMounts: + - name: empty-dir + mountPath: /tmp + subPath: tmp-dir + - name: redis-data + mountPath: {{ .Values.replica.persistence.path }} + {{- if .Values.replica.persistence.subPath }} + subPath: {{ .Values.replica.persistence.subPath }} + {{- else if .Values.replica.persistence.subPathExpr }} + subPathExpr: {{ .Values.replica.persistence.subPathExpr }} + {{- end }} + {{- end }} + {{- if .Values.sysctl.enabled }} + - name: init-sysctl + image: {{ include "redis.sysctl.image" . }} + imagePullPolicy: {{ default "" .Values.sysctl.image.pullPolicy | quote }} + securityContext: + privileged: true + runAsUser: 0 + {{- if .Values.sysctl.command }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.sysctl.command "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.sysctl.resources }} + resources: {{- toYaml .Values.sysctl.resources | nindent 12 }} + {{- else if ne .Values.sysctl.resourcesPreset "none" }} + resources: {{- include "common.resources.preset" (dict "type" .Values.sysctl.resourcesPreset) | nindent 12 }} + {{- end }} + {{- if .Values.sysctl.mountHostSys }} + volumeMounts: + - name: empty-dir + mountPath: /tmp + subPath: tmp-dir + - name: host-sys + mountPath: /host-sys + {{- end }} + {{- end }} + {{- end }} + volumes: + - name: start-scripts + configMap: + name: {{ printf "%s-scripts" (include "common.names.fullname" .) }} + defaultMode: 0755 + - name: health + configMap: + name: {{ printf "%s-health" (include "common.names.fullname" .) }} + defaultMode: 0755 + {{- if .Values.auth.usePasswordFiles }} + - name: redis-password + {{ if .Values.auth.usePasswordFileFromSecret }} + secret: + secretName: {{ template "redis.secretName" . }} + items: + - key: {{ template "redis.secretPasswordKey" . }} + path: redis-password + {{- else }} + emptyDir: {} + {{- end }} + {{- end }} + - name: config + configMap: + name: {{ include "redis.configmapName" . }} + {{- if .Values.sysctl.mountHostSys }} + - name: host-sys + hostPath: + path: /sys + {{- end }} + - name: empty-dir + {{- if or .Values.replica.persistence.medium .Values.replica.persistence.sizeLimit }} + emptyDir: + {{- if .Values.replica.persistence.medium }} + medium: {{ .Values.replica.persistence.medium | quote }} + {{- end }} + {{- if .Values.replica.persistence.sizeLimit }} + sizeLimit: {{ .Values.replica.persistence.sizeLimit | quote }} + {{- end }} + {{- else }} + emptyDir: {} + {{- end }} + {{- if .Values.tls.enabled }} + - name: redis-certificates + secret: + secretName: {{ include "redis.tlsSecretName" . }} + defaultMode: 256 + {{- end }} + {{- if .Values.replica.extraVolumes }} + {{- include "common.tplvalues.render" ( dict "value" .Values.replica.extraVolumes "context" $ ) | nindent 8 }} + {{- end }} + {{- if .Values.metrics.extraVolumes }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.extraVolumes "context" $ ) | nindent 8 }} + {{- end }} + {{- if or (not .Values.replica.persistence.enabled) (not (eq .Values.replica.kind "StatefulSet")) }} + - name: redis-data + {{- if or .Values.replica.persistence.medium .Values.replica.persistence.sizeLimit }} + emptyDir: + {{- if .Values.replica.persistence.medium }} + medium: {{ .Values.replica.persistence.medium | quote }} + {{- end }} + {{- if .Values.replica.persistence.sizeLimit }} + sizeLimit: {{ .Values.replica.persistence.sizeLimit | quote }} + {{- end }} + {{- else }} + emptyDir: {} + {{- end }} + {{- else if .Values.replica.persistence.existingClaim }} + - name: redis-data + persistentVolumeClaim: + claimName: {{ printf "%s" (tpl .Values.replica.persistence.existingClaim .) }} + {{- else }} + {{- if .Values.replica.persistentVolumeClaimRetentionPolicy.enabled }} + persistentVolumeClaimRetentionPolicy: + whenDeleted: {{ .Values.replica.persistentVolumeClaimRetentionPolicy.whenDeleted }} + whenScaled: {{ .Values.replica.persistentVolumeClaimRetentionPolicy.whenScaled }} + {{- end }} + volumeClaimTemplates: + - apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + name: redis-data + {{- $claimLabels := include "common.tplvalues.merge" ( dict "values" ( list .Values.master.persistence.labels .Values.commonLabels ) "context" . ) }} + labels: {{- include "common.labels.matchLabels" ( dict "customLabels" $claimLabels "context" $ ) | nindent 10 }} + app.kubernetes.io/component: replica + {{- if .Values.replica.persistence.annotations }} + annotations: {{- toYaml .Values.replica.persistence.annotations | nindent 10 }} + {{- end }} + spec: + accessModes: + {{- range .Values.replica.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.replica.persistence.size | quote }} + {{- if .Values.replica.persistence.selector }} + selector: {{- include "common.tplvalues.render" (dict "value" .Values.replica.persistence.selector "context" $) | nindent 10 }} + {{- end }} + {{- if .Values.replica.persistence.dataSource }} + dataSource: {{- include "common.tplvalues.render" (dict "value" .Values.replica.persistence.dataSource "context" $) | nindent 10 }} + {{- end }} + {{- include "common.storage.class" (dict "persistence" .Values.replica.persistence "global" .Values.global) | nindent 8 }} + {{- end }} +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/replicas/hpa.yaml b/charts/open-webui/charts/redis/templates/replicas/hpa.yaml new file mode 100644 index 0000000..85adf72 --- /dev/null +++ b/charts/open-webui/charts/redis/templates/replicas/hpa.yaml @@ -0,0 +1,49 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if and .Values.replica.autoscaling.enabled (not .Values.sentinel.enabled) }} +apiVersion: {{ include "common.capabilities.hpa.apiVersion" ( dict "context" $ ) }} +kind: HorizontalPodAutoscaler +metadata: + name: {{ printf "%s-replicas" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: replica + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + scaleTargetRef: + apiVersion: {{ include "common.capabilities.deployment.apiVersion" . }} + kind: StatefulSet + name: {{ printf "%s-replicas" (include "common.names.fullname" .) }} + minReplicas: {{ .Values.replica.autoscaling.minReplicas }} + maxReplicas: {{ .Values.replica.autoscaling.maxReplicas }} + metrics: + {{- if .Values.replica.autoscaling.targetCPU }} + - type: Resource + resource: + name: cpu + {{- if semverCompare "<1.23-0" (include "common.capabilities.kubeVersion" .) }} + targetAverageUtilization: {{ .Values.replica.autoscaling.targetCPU }} + {{- else }} + target: + type: Utilization + averageUtilization: {{ .Values.replica.autoscaling.targetCPU }} + {{- end }} + {{- end }} + {{- if .Values.replica.autoscaling.targetMemory }} + - type: Resource + resource: + name: memory + {{- if semverCompare "<1.23-0" (include "common.capabilities.kubeVersion" .) }} + targetAverageUtilization: {{ .Values.replica.autoscaling.targetMemory }} + {{- else }} + target: + type: Utilization + averageUtilization: {{ .Values.replica.autoscaling.targetMemory }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/replicas/pdb.yaml b/charts/open-webui/charts/redis/templates/replicas/pdb.yaml new file mode 100644 index 0000000..d7b777b --- /dev/null +++ b/charts/open-webui/charts/redis/templates/replicas/pdb.yaml @@ -0,0 +1,28 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- $pdb := coalesce .Values.pdb .Values.replica.pdb }} +{{- if and (eq .Values.architecture "replication") (not .Values.sentinel.enabled) $pdb.create }} +apiVersion: {{ include "common.capabilities.policy.apiVersion" . }} +kind: PodDisruptionBudget +metadata: + name: {{ printf "%s-replicas" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: replica + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + {{- if $pdb.minAvailable }} + minAvailable: {{ $pdb.minAvailable }} + {{- end }} + {{- if or $pdb.maxUnavailable (not $pdb.minAvailable) }} + maxUnavailable: {{ $pdb.maxUnavailable | default 1 }} + {{- end }} + selector: + matchLabels: {{- include "common.labels.matchLabels" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 6 }} + app.kubernetes.io/component: replica +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/replicas/service.yaml b/charts/open-webui/charts/redis/templates/replicas/service.yaml new file mode 100644 index 0000000..ebb2a4f --- /dev/null +++ b/charts/open-webui/charts/redis/templates/replicas/service.yaml @@ -0,0 +1,59 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if and (eq .Values.architecture "replication") (not .Values.sentinel.enabled) }} +apiVersion: v1 +kind: Service +metadata: + name: {{ printf "%s-replicas" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: replica + {{- if or .Values.replica.service.annotations .Values.commonAnnotations }} + {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list .Values.replica.service.annotations .Values.commonAnnotations ) "context" . ) }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.replica.service.type }} + {{- if or (eq .Values.replica.service.type "LoadBalancer") (eq .Values.replica.service.type "NodePort") }} + externalTrafficPolicy: {{ .Values.replica.service.externalTrafficPolicy | quote }} + {{- end }} + {{- if (semverCompare ">=1.22-0" (include "common.capabilities.kubeVersion" .)) }} + internalTrafficPolicy: {{ .Values.replica.service.internalTrafficPolicy }} + {{- end }} + {{- if and (eq .Values.replica.service.type "LoadBalancer") (not (empty .Values.replica.service.loadBalancerIP)) }} + loadBalancerIP: {{ .Values.replica.service.loadBalancerIP }} + {{- end }} + {{- if and (eq .Values.replica.service.type "LoadBalancer") .Values.replica.service.loadBalancerClass }} + loadBalancerClass: {{ .Values.replica.service.loadBalancerClass }} + {{- end }} + {{- if and (eq .Values.replica.service.type "LoadBalancer") (not (empty .Values.replica.service.loadBalancerSourceRanges)) }} + loadBalancerSourceRanges: {{ toYaml .Values.replica.service.loadBalancerSourceRanges | nindent 4 }} + {{- end }} + {{- if and .Values.replica.service.clusterIP (eq .Values.replica.service.type "ClusterIP") }} + clusterIP: {{ .Values.replica.service.clusterIP }} + {{- end }} + {{- if .Values.replica.service.sessionAffinity }} + sessionAffinity: {{ .Values.replica.service.sessionAffinity }} + {{- end }} + {{- if .Values.replica.service.sessionAffinityConfig }} + sessionAffinityConfig: {{- include "common.tplvalues.render" (dict "value" .Values.replica.service.sessionAffinityConfig "context" $) | nindent 4 }} + {{- end }} + ports: + - name: tcp-redis + port: {{ .Values.replica.service.ports.redis }} + targetPort: redis + {{- if and (or (eq .Values.replica.service.type "NodePort") (eq .Values.replica.service.type "LoadBalancer")) .Values.replica.service.nodePorts.redis}} + nodePort: {{ .Values.replica.service.nodePorts.redis}} + {{- else if eq .Values.replica.service.type "ClusterIP" }} + nodePort: null + {{- end }} + {{- if .Values.replica.service.extraPorts }} + {{- include "common.tplvalues.render" (dict "value" .Values.replica.service.extraPorts "context" $) | nindent 4 }} + {{- end }} + {{- $podLabels := include "common.tplvalues.merge" ( dict "values" ( list .Values.replica.podLabels .Values.commonLabels ) "context" . ) }} + selector: {{- include "common.labels.matchLabels" ( dict "customLabels" $podLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: replica +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/replicas/serviceaccount.yaml b/charts/open-webui/charts/redis/templates/replicas/serviceaccount.yaml new file mode 100644 index 0000000..6cf3411 --- /dev/null +++ b/charts/open-webui/charts/redis/templates/replicas/serviceaccount.yaml @@ -0,0 +1,18 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if and .Values.replica.serviceAccount.create (eq .Values.architecture "replication") (not .Values.sentinel.enabled) }} +apiVersion: v1 +kind: ServiceAccount +automountServiceAccountToken: {{ .Values.replica.serviceAccount.automountServiceAccountToken }} +metadata: + name: {{ template "redis.replicaServiceAccountName" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if or .Values.replica.serviceAccount.annotations .Values.commonAnnotations }} + {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list .Values.replica.serviceAccount.annotations .Values.commonAnnotations ) "context" . ) }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/role.yaml b/charts/open-webui/charts/redis/templates/role.yaml new file mode 100644 index 0000000..54a2b1b --- /dev/null +++ b/charts/open-webui/charts/redis/templates/role.yaml @@ -0,0 +1,34 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if .Values.rbac.create }} +apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} +kind: Role +metadata: + name: {{ template "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +rules: + {{- if and (include "common.capabilities.psp.supported" .) .Values.podSecurityPolicy.enabled }} + - apiGroups: + - '{{ template "podSecurityPolicy.apiGroup" . }}' + resources: + - 'podsecuritypolicies' + verbs: + - 'use' + resourceNames: [{{ printf "%s-master" (include "common.names.fullname" .) }}] + {{- end }} + {{- if and .Values.sentinel.enabled (or .Values.sentinel.masterService.enabled .Values.sentinel.service.createMaster) }} + - apiGroups: [""] + resources: ["pods"] + verbs: ["list", "patch"] + {{- end -}} + {{- if .Values.rbac.rules }} + {{- include "common.tplvalues.render" ( dict "value" .Values.rbac.rules "context" $ ) | nindent 2 }} + {{- end }} +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/rolebinding.yaml b/charts/open-webui/charts/redis/templates/rolebinding.yaml new file mode 100644 index 0000000..a164289 --- /dev/null +++ b/charts/open-webui/charts/redis/templates/rolebinding.yaml @@ -0,0 +1,23 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if .Values.rbac.create }} +apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} +kind: RoleBinding +metadata: + name: {{ template "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "common.names.fullname" . }} +subjects: + - kind: ServiceAccount + name: {{ template "redis.serviceAccountName" . }} +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/scripts-configmap.yaml b/charts/open-webui/charts/redis/templates/scripts-configmap.yaml new file mode 100644 index 0000000..d1d277b --- /dev/null +++ b/charts/open-webui/charts/redis/templates/scripts-configmap.yaml @@ -0,0 +1,811 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ printf "%s-scripts" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +data: +{{- if and (eq .Values.architecture "replication") .Values.sentinel.enabled }} + start-node.sh: | + #!/bin/bash + + . /opt/bitnami/scripts/libos.sh + . /opt/bitnami/scripts/liblog.sh + . /opt/bitnami/scripts/libvalidations.sh + + get_port() { + hostname="$1" + type="$2" + + port_var=$(echo "${hostname^^}_SERVICE_PORT_$type" | sed "s/-/_/g") + port=${!port_var} + + if [ -z "$port" ]; then + case $type in + "SENTINEL") + echo {{ .Values.sentinel.containerPorts.sentinel }} + ;; + "REDIS") + echo {{ .Values.master.containerPorts.redis }} + ;; + esac + else + echo $port + fi + } + + get_full_hostname() { + hostname="$1" + + {{- if .Values.useExternalDNS.enabled }} + full_hostname="${hostname}.{{- include "redis.externalDNS.suffix" . }}" + {{- else if eq .Values.sentinel.service.type "NodePort" }} + full_hostname="${hostname}.{{- include "common.names.namespace" . }}" + {{- else }} + full_hostname="${hostname}.${HEADLESS_SERVICE}" + {{- end }} + + {{- if .Values.useHostnames }} + echo "${full_hostname}" + {{- else }} + retry_count=0 + until getent hosts "${full_hostname}" | awk '{ print $1; exit }' | grep .; do + if [[ $retry_count -lt {{ .Values.nameResolutionThreshold }} ]]; then + sleep {{ .Values.nameResolutionTimeout }} + else + error "IP address for ${full_hostname} not found" + exit 1 + fi + ((retry_count++)) + done + {{- end }} + } + + REDISPORT=$(get_port "$HOSTNAME" "REDIS") + + HEADLESS_SERVICE="{{ template "common.names.fullname" . }}-headless.{{ include "common.names.namespace" . }}.svc.{{ .Values.clusterDomain }}" + + if [ -n "$REDIS_EXTERNAL_MASTER_HOST" ]; then + REDIS_SERVICE="$REDIS_EXTERNAL_MASTER_HOST" + else + REDIS_SERVICE="{{ template "common.names.fullname" . }}.{{ include "common.names.namespace" . }}.svc.{{ .Values.clusterDomain }}" + fi + + SENTINEL_SERVICE_PORT=$(get_port "{{ include "common.names.fullname" . }}" "SENTINEL") + + redis_cli_command() { + local timeout="${1:-0}" + + local args=("-h" "$REDIS_SERVICE" "-p" "$SENTINEL_SERVICE_PORT") + local command="redis-cli" + if is_boolean_yes "$REDIS_TLS_ENABLED"; then + args+=("--tls" "--cert" "$REDIS_TLS_CERT_FILE" "--key" "$REDIS_TLS_KEY_FILE") + [ -n "$REDIS_TLS_CA_FILE" ] && args+=("--cacert" "$REDIS_TLS_CA_FILE") + fi + if [ "$timeout" -gt 0 ]; then + command="timeout $timeout $command" + fi + + echo "{{- if and .Values.auth.enabled .Values.auth.sentinel }}REDISCLI_AUTH="\$REDIS_PASSWORD" {{ end }} $command ${args[*]}" + } + + validate_quorum() { + quorum_info_command="$(redis_cli_command) sentinel master {{ .Values.sentinel.masterSet }}" + info "about to run the command: $quorum_info_command" + eval $quorum_info_command | grep -Fq "s_down" + } + + trigger_manual_failover() { + failover_command="$(redis_cli_command) sentinel failover {{ .Values.sentinel.masterSet }}" + info "about to run the command: $failover_command" + eval $failover_command + } + + get_sentinel_master_info() { + sentinel_info_command="$(redis_cli_command {{ .Values.sentinel.getMasterTimeout }}) sentinel get-master-addr-by-name {{ .Values.sentinel.masterSet }}" + info "about to run the command: $sentinel_info_command" + retry_while "eval $sentinel_info_command" 2 5 + } + + {{- if and .Values.replica.containerSecurityContext.runAsUser (eq (.Values.replica.containerSecurityContext.runAsUser | int) 0) }} + useradd redis + chown -R redis {{ .Values.replica.persistence.path }} + {{- end }} + + [[ -f $REDIS_PASSWORD_FILE ]] && export REDIS_PASSWORD="$(< "${REDIS_PASSWORD_FILE}")" + [[ -f $REDIS_MASTER_PASSWORD_FILE ]] && export REDIS_MASTER_PASSWORD="$(< "${REDIS_MASTER_PASSWORD_FILE}")" + + # check if there is a master + master_in_persisted_conf="$(get_full_hostname "$HOSTNAME")" + master_port_in_persisted_conf="$REDIS_MASTER_PORT_NUMBER" + master_in_sentinel="$(get_sentinel_master_info)" + redisRetVal=$? + + if [[ -f /opt/bitnami/redis-sentinel/etc/sentinel.conf ]]; then + master_in_persisted_conf="$(awk '/monitor/ {print $4}' /opt/bitnami/redis-sentinel/etc/sentinel.conf)" + master_port_in_persisted_conf="$(awk '/monitor/ {print $5}' /opt/bitnami/redis-sentinel/etc/sentinel.conf)" + info "Found previous master ${master_in_persisted_conf}:${master_port_in_persisted_conf} in /opt/bitnami/redis-sentinel/etc/sentinel.conf" + debug "$(cat /opt/bitnami/redis-sentinel/etc/sentinel.conf | grep monitor)" + fi + + if [[ $redisRetVal -ne 0 ]]; then + if [[ "$master_in_persisted_conf" == "$(get_full_hostname "$HOSTNAME")" ]]; then + # Case 1: No active sentinel and in previous sentinel.conf we were the master --> MASTER + info "Configuring the node as master" + export REDIS_REPLICATION_MODE="master" + else + # Case 2: No active sentinel and in previous sentinel.conf we were not master --> REPLICA + info "Configuring the node as replica" + export REDIS_REPLICATION_MODE="replica" + REDIS_MASTER_HOST=${master_in_persisted_conf} + REDIS_MASTER_PORT_NUMBER=${master_port_in_persisted_conf} + fi + else + # Fetches current master's host and port + REDIS_SENTINEL_INFO=($(get_sentinel_master_info)) + info "Current master: REDIS_SENTINEL_INFO=(${REDIS_SENTINEL_INFO[0]},${REDIS_SENTINEL_INFO[1]})" + REDIS_MASTER_HOST=${REDIS_SENTINEL_INFO[0]} + REDIS_MASTER_PORT_NUMBER=${REDIS_SENTINEL_INFO[1]} + + if [[ "$REDIS_MASTER_HOST" == "$(get_full_hostname "$HOSTNAME")" ]]; then + # Case 3: Active sentinel and master it is this node --> MASTER + info "Configuring the node as master" + export REDIS_REPLICATION_MODE="master" + else + # Case 4: Active sentinel and master is not this node --> REPLICA + info "Configuring the node as replica" + export REDIS_REPLICATION_MODE="replica" + + {{- if and .Values.sentinel.automateClusterRecovery (le (int .Values.sentinel.downAfterMilliseconds) 2000) }} + retry_count=1 + while validate_quorum + do + info "sleeping, waiting for Redis master to come up" + sleep 1s + if ! ((retry_count % 11)); then + info "Trying to manually failover" + failover_result=$(trigger_manual_failover) + + debug "Failover result: $failover_result" + fi + + ((retry_count+=1)) + done + info "Redis master is up now" + {{- end }} + fi + fi + + if [[ -n "$REDIS_EXTERNAL_MASTER_HOST" ]]; then + REDIS_MASTER_HOST="$REDIS_EXTERNAL_MASTER_HOST" + REDIS_MASTER_PORT_NUMBER="${REDIS_EXTERNAL_MASTER_PORT}" + fi + + if [[ -f /opt/bitnami/redis/mounted-etc/replica.conf ]];then + cp /opt/bitnami/redis/mounted-etc/replica.conf /opt/bitnami/redis/etc/replica.conf + fi + + if [[ -f /opt/bitnami/redis/mounted-etc/redis.conf ]];then + cp /opt/bitnami/redis/mounted-etc/redis.conf /opt/bitnami/redis/etc/redis.conf + fi + + echo "" >> /opt/bitnami/redis/etc/replica.conf + echo "replica-announce-port $REDISPORT" >> /opt/bitnami/redis/etc/replica.conf + echo "replica-announce-ip $(get_full_hostname "$HOSTNAME")" >> /opt/bitnami/redis/etc/replica.conf + + {{- if .Values.tls.enabled }} + ARGS=("--port" "0") + ARGS+=("--tls-port" "${REDIS_TLS_PORT}") + ARGS+=("--tls-cert-file" "${REDIS_TLS_CERT_FILE}") + ARGS+=("--tls-key-file" "${REDIS_TLS_KEY_FILE}") + {{- if not (empty (include "redis.tlsCACert" .)) }} + ARGS+=("--tls-ca-cert-file" "${REDIS_TLS_CA_FILE}") + {{- end }} + ARGS+=("--tls-auth-clients" "${REDIS_TLS_AUTH_CLIENTS}") + ARGS+=("--tls-replication" "yes") + {{- if .Values.tls.dhParamsFilename }} + ARGS+=("--tls-dh-params-file" "${REDIS_TLS_DH_PARAMS_FILE}") + {{- end }} + {{- else }} + ARGS=("--port" "${REDIS_PORT}") + {{- end }} + + if [[ "$REDIS_REPLICATION_MODE" = "slave" ]] || [[ "$REDIS_REPLICATION_MODE" = "replica" ]]; then + ARGS+=("--replicaof" "${REDIS_MASTER_HOST}" "${REDIS_MASTER_PORT_NUMBER}") + fi + + {{- if .Values.auth.enabled }} + ARGS+=("--requirepass" "${REDIS_PASSWORD}") + ARGS+=("--masterauth" "${REDIS_MASTER_PASSWORD}") + {{- else }} + ARGS+=("--protected-mode" "no") + {{- end }} + ARGS+=("--include" "/opt/bitnami/redis/etc/replica.conf") + ARGS+=("--include" "/opt/bitnami/redis/etc/redis.conf") + {{- if .Values.replica.extraFlags }} + {{- range .Values.replica.extraFlags }} + ARGS+=({{ . | quote }}) + {{- end }} + {{- end }} + + {{- if .Values.replica.preExecCmds }} + {{- range $command := .Values.replica.preExecCmds }} + {{- $command | nindent 4 }} + {{- end }} + {{- end }} + + {{- if .Values.replica.command }} + exec {{ .Values.replica.command }} "${ARGS[@]}" + {{- else }} + exec redis-server "${ARGS[@]}" + {{- end }} + + start-sentinel.sh: | + #!/bin/bash + + . /opt/bitnami/scripts/libos.sh + . /opt/bitnami/scripts/libvalidations.sh + . /opt/bitnami/scripts/libfile.sh + + HEADLESS_SERVICE="{{ template "common.names.fullname" . }}-headless.{{ include "common.names.namespace" . }}.svc.{{ .Values.clusterDomain }}" + REDIS_SERVICE="{{ template "common.names.fullname" . }}.{{ include "common.names.namespace" . }}.svc.{{ .Values.clusterDomain }}" + + get_port() { + hostname="$1" + type="$2" + + port_var=$(echo "${hostname^^}_SERVICE_PORT_$type" | sed "s/-/_/g") + port=${!port_var} + + if [ -z "$port" ]; then + case $type in + "SENTINEL") + echo {{ .Values.sentinel.containerPorts.sentinel }} + ;; + "REDIS") + echo {{ .Values.master.containerPorts.redis }} + ;; + esac + else + echo $port + fi + } + + get_full_hostname() { + hostname="$1" + + {{- if .Values.useExternalDNS.enabled }} + full_hostname="${hostname}.{{- include "redis.externalDNS.suffix" . }}" + {{- else if eq .Values.sentinel.service.type "NodePort" }} + full_hostname="${hostname}.{{- include "common.names.namespace" . }}" + {{- else }} + full_hostname="${hostname}.${HEADLESS_SERVICE}" + {{- end }} + + {{- if .Values.useHostnames }} + echo "${full_hostname}" + {{- else }} + retry_count=0 + until getent hosts "${full_hostname}" | awk '{ print $1; exit }' | grep .; do + if [[ $retry_count -lt {{ .Values.nameResolutionThreshold }} ]]; then + sleep {{ .Values.nameResolutionTimeout }} + else + error "IP address for ${full_hostname} not found" + exit 1 + fi + ((retry_count++)) + done + {{- end }} + } + + SERVPORT=$(get_port "$HOSTNAME" "SENTINEL") + REDISPORT=$(get_port "$HOSTNAME" "REDIS") + SENTINEL_SERVICE_PORT=$(get_port "{{ include "common.names.fullname" . }}" "SENTINEL") + + sentinel_conf_set() { + local -r key="${1:?missing key}" + local value="${2:-}" + + # Sanitize inputs + value="${value//\\/\\\\}" + value="${value//&/\\&}" + value="${value//\?/\\?}" + [[ "$value" = "" ]] && value="\"$value\"" + + replace_in_file "/opt/bitnami/redis-sentinel/etc/prepare-sentinel.conf" "^#*\s*${key} .*" "${key} ${value}" false + } + sentinel_conf_add() { + echo $'\n'"$@" >> "/opt/bitnami/redis-sentinel/etc/prepare-sentinel.conf" + } + host_id() { + echo "$1" | openssl sha1 | awk '{print $2}' + } + get_sentinel_master_info() { + if is_boolean_yes "$REDIS_SENTINEL_TLS_ENABLED"; then + sentinel_info_command="{{- if and .Values.auth.enabled .Values.auth.sentinel }}REDISCLI_AUTH="\$REDIS_PASSWORD" {{ end }}timeout {{ .Values.sentinel.getMasterTimeout }} redis-cli -h $REDIS_SERVICE -p $SENTINEL_SERVICE_PORT --tls --cert ${REDIS_SENTINEL_TLS_CERT_FILE} --key ${REDIS_SENTINEL_TLS_KEY_FILE} --cacert ${REDIS_SENTINEL_TLS_CA_FILE} sentinel get-master-addr-by-name {{ .Values.sentinel.masterSet }}" + else + sentinel_info_command="{{- if and .Values.auth.enabled .Values.auth.sentinel }}REDISCLI_AUTH="\$REDIS_PASSWORD" {{ end }}timeout {{ .Values.sentinel.getMasterTimeout }} redis-cli -h $REDIS_SERVICE -p $SENTINEL_SERVICE_PORT sentinel get-master-addr-by-name {{ .Values.sentinel.masterSet }}" + fi + info "about to run the command: $sentinel_info_command" + retry_while "eval $sentinel_info_command" 2 5 + } + + [[ -f $REDIS_PASSWORD_FILE ]] && export REDIS_PASSWORD="$(< "${REDIS_PASSWORD_FILE}")" + + master_in_persisted_conf="$(get_full_hostname "$HOSTNAME")" + + if [[ -f /opt/bitnami/redis-sentinel/etc/sentinel.conf ]]; then + master_in_persisted_conf="$(awk '/monitor/ {print $4}' /opt/bitnami/redis-sentinel/etc/sentinel.conf)" + info "Found previous master $master_in_persisted_conf in /opt/bitnami/redis-sentinel/etc/sentinel.conf" + debug "$(cat /opt/bitnami/redis-sentinel/etc/sentinel.conf | grep monitor)" + fi + REDIS_SENTINEL_INFO=($(get_sentinel_master_info)) + if [ "$?" -eq "0" ]; then + # current master's host and port obtained from other Sentinel + info "printing REDIS_SENTINEL_INFO=(${REDIS_SENTINEL_INFO[0]},${REDIS_SENTINEL_INFO[1]})" + REDIS_MASTER_HOST=${REDIS_SENTINEL_INFO[0]} + REDIS_MASTER_PORT_NUMBER=${REDIS_SENTINEL_INFO[1]} + else + REDIS_MASTER_HOST="$master_in_persisted_conf" + REDIS_MASTER_PORT_NUMBER="$REDISPORT" + fi + if [[ "$REDIS_MASTER_HOST" == "$(get_full_hostname "$HOSTNAME")" ]]; then + export REDIS_REPLICATION_MODE="master" + else + export REDIS_REPLICATION_MODE="replica" + fi + + {{- if or .Values.sentinel.masterService.enabled .Values.sentinel.service.createMaster }} + if [[ "${REDIS_REPLICATION_MODE}" == "master" ]]; then + # Add isMaster label to master node for master service + echo "${REDIS_MASTER_HOST/.*}" > /etc/shared/current + fi + {{- end }} + + if [[ -n "$REDIS_EXTERNAL_MASTER_HOST" ]]; then + REDIS_MASTER_HOST="$REDIS_EXTERNAL_MASTER_HOST" + REDIS_MASTER_PORT_NUMBER="${REDIS_EXTERNAL_MASTER_PORT}" + fi + + # To prevent incomplete configuration and as the redis container accesses /opt/bitnami/redis-sentinel/etc/sentinel.conf + # as well, prepare the new config in `prepare-sentinel.conf` and move it atomically to the ultimate destination when it is complete. + cp /opt/bitnami/redis-sentinel/mounted-etc/sentinel.conf /opt/bitnami/redis-sentinel/etc/prepare-sentinel.conf + {{- if .Values.auth.enabled }} + printf "\nsentinel auth-pass %s %s" "{{ .Values.sentinel.masterSet }}" "$REDIS_PASSWORD" >> /opt/bitnami/redis-sentinel/etc/prepare-sentinel.conf + {{- if and .Values.auth.enabled .Values.auth.sentinel }} + printf "\nrequirepass %s" "$REDIS_PASSWORD" >> /opt/bitnami/redis-sentinel/etc/prepare-sentinel.conf + {{- end }} + {{- end }} + printf "\nsentinel myid %s" "$(host_id "$HOSTNAME")" >> /opt/bitnami/redis-sentinel/etc/prepare-sentinel.conf + + if [[ -z "$REDIS_MASTER_HOST" ]] || [[ -z "$REDIS_MASTER_PORT_NUMBER" ]] + then + # Prevent incorrect configuration to be written to sentinel.conf + error "Redis master host is configured incorrectly (host: $REDIS_MASTER_HOST, port: $REDIS_MASTER_PORT_NUMBER)" + exit 1 + fi + + sentinel_conf_set "sentinel monitor" "{{ .Values.sentinel.masterSet }} "$REDIS_MASTER_HOST" "$REDIS_MASTER_PORT_NUMBER" {{ .Values.sentinel.quorum }}" + + add_known_sentinel() { + hostname="$1" + ip="$2" + + if [[ -n "$hostname" && -n "$ip" && "$hostname" != "$HOSTNAME" ]]; then + sentinel_conf_add "sentinel known-sentinel {{ .Values.sentinel.masterSet }} $(get_full_hostname "$hostname") $(get_port "$hostname" "SENTINEL") $(host_id "$hostname")" + fi + } + add_known_replica() { + hostname="$1" + ip="$2" + + if [[ -n "$ip" && "$(get_full_hostname "$hostname")" != "$REDIS_MASTER_HOST" ]]; then + sentinel_conf_add "sentinel known-replica {{ .Values.sentinel.masterSet }} $(get_full_hostname "$hostname") $(get_port "$hostname" "REDIS")" + fi + } + + # Add available hosts on the network as known replicas & sentinels + for node in $(seq 0 $(({{ .Values.replica.replicaCount }}-1))); do + hostname="{{ template "common.names.fullname" . }}-node-$node" + ip="$(getent hosts "$hostname.$HEADLESS_SERVICE" | awk '{ print $1 }')" + add_known_sentinel "$hostname" "$ip" + add_known_replica "$hostname" "$ip" + done + + echo "" >> /opt/bitnami/redis-sentinel/etc/prepare-sentinel.conf + {{- if not (contains "sentinel announce-hostnames" .Values.sentinel.configuration) }} + echo "sentinel announce-hostnames yes" >> /opt/bitnami/redis-sentinel/etc/prepare-sentinel.conf + {{- end }} + {{- if not (contains "sentinel resolve-hostnames" .Values.sentinel.configuration) }} + echo "sentinel resolve-hostnames yes" >> /opt/bitnami/redis-sentinel/etc/prepare-sentinel.conf + {{- end }} + {{- if not (contains "sentinel announce-port" .Values.sentinel.configuration) }} + echo "sentinel announce-port $SERVPORT" >> /opt/bitnami/redis-sentinel/etc/prepare-sentinel.conf + {{- end }} + {{- if not (contains "sentinel announce-ip" .Values.sentinel.configuration) }} + echo "sentinel announce-ip $(get_full_hostname "$HOSTNAME")" >> /opt/bitnami/redis-sentinel/etc/prepare-sentinel.conf + {{- end }} + + {{- if .Values.tls.enabled }} + ARGS=("--port" "0") + ARGS+=("--tls-port" "${REDIS_SENTINEL_TLS_PORT_NUMBER}") + ARGS+=("--tls-cert-file" "${REDIS_SENTINEL_TLS_CERT_FILE}") + ARGS+=("--tls-key-file" "${REDIS_SENTINEL_TLS_KEY_FILE}") + ARGS+=("--tls-ca-cert-file" "${REDIS_SENTINEL_TLS_CA_FILE}") + ARGS+=("--tls-replication" "yes") + ARGS+=("--tls-auth-clients" "${REDIS_SENTINEL_TLS_AUTH_CLIENTS}") + {{- if .Values.tls.dhParamsFilename }} + ARGS+=("--tls-dh-params-file" "${REDIS_SENTINEL_TLS_DH_PARAMS_FILE}") + {{- end }} + {{- end }} + {{- if .Values.sentinel.preExecCmds }} + {{- range $command := .Values.sentinel.preExecCmds }} + {{- $command | nindent 4 }} + {{- end }} + {{- end }} + mv /opt/bitnami/redis-sentinel/etc/prepare-sentinel.conf /opt/bitnami/redis-sentinel/etc/sentinel.conf + exec redis-server /opt/bitnami/redis-sentinel/etc/sentinel.conf {{- if .Values.tls.enabled }} "${ARGS[@]}" {{- end }} --sentinel + prestop-sentinel.sh: | + #!/bin/bash + + . /opt/bitnami/scripts/libvalidations.sh + . /opt/bitnami/scripts/libos.sh + + HEADLESS_SERVICE="{{ template "common.names.fullname" . }}-headless.{{ include "common.names.namespace" . }}.svc.{{ .Values.clusterDomain }}" + + get_full_hostname() { + hostname="$1" + + {{- if .Values.useExternalDNS.enabled }} + full_hostname="${hostname}.{{- include "redis.externalDNS.suffix" . }}" + {{- else if eq .Values.sentinel.service.type "NodePort" }} + full_hostname="${hostname}.{{- include "common.names.namespace" . }}" + {{- else }} + full_hostname="${hostname}.${HEADLESS_SERVICE}" + {{- end }} + + {{- if .Values.useHostnames }} + echo "${full_hostname}" + {{- else }} + retry_count=0 + until getent hosts "${full_hostname}" | awk '{ print $1; exit }' | grep .; do + if [[ $retry_count -lt {{ .Values.nameResolutionThreshold }} ]]; then + sleep {{ .Values.nameResolutionTimeout }} + else + error "IP address for ${full_hostname} not found" + exit 1 + fi + ((retry_count++)) + done + {{- end }} + } + + run_sentinel_command() { + if is_boolean_yes "$REDIS_SENTINEL_TLS_ENABLED"; then + redis-cli -h "$REDIS_SERVICE" -p "$REDIS_SENTINEL_TLS_PORT_NUMBER" --tls --cert "$REDIS_SENTINEL_TLS_CERT_FILE" --key "$REDIS_SENTINEL_TLS_KEY_FILE" --cacert "$REDIS_SENTINEL_TLS_CA_FILE" sentinel "$@" + else + redis-cli -h "$REDIS_SERVICE" -p "$REDIS_SENTINEL_PORT" sentinel "$@" + fi + } + sentinel_failover_finished() { + REDIS_SENTINEL_INFO=($(run_sentinel_command get-master-addr-by-name "{{ .Values.sentinel.masterSet }}")) + REDIS_MASTER_HOST="${REDIS_SENTINEL_INFO[0]}" + [[ "$REDIS_MASTER_HOST" != "$(get_full_hostname $HOSTNAME)" ]] + } + + REDIS_SERVICE="{{ include "common.names.fullname" . }}.{{ include "common.names.namespace" . }}.svc.{{ .Values.clusterDomain }}" + + {{ if .Values.auth.sentinel -}} + # redis-cli automatically consumes credentials from the REDISCLI_AUTH variable + [[ -n "$REDIS_PASSWORD" ]] && export REDISCLI_AUTH="$REDIS_PASSWORD" + [[ -f "$REDIS_PASSWORD_FILE" ]] && export REDISCLI_AUTH="$(< "${REDIS_PASSWORD_FILE}")" + {{- end }} + + if ! sentinel_failover_finished; then + echo "I am the master pod and you are stopping me. Starting sentinel failover" + if retry_while "sentinel_failover_finished" "{{ sub .Values.sentinel.terminationGracePeriodSeconds 10 }}" 1; then + echo "Master has been successfuly failed over to a different pod." + exit 0 + else + echo "Master failover failed" + exit 1 + fi + else + exit 0 + fi + prestop-redis.sh: | + #!/bin/bash + + . /opt/bitnami/scripts/libvalidations.sh + . /opt/bitnami/scripts/libos.sh + + run_redis_command() { + local args=("-h" "127.0.0.1") + if is_boolean_yes "$REDIS_TLS_ENABLED"; then + args+=("-p" "$REDIS_TLS_PORT" "--tls" "--cert" "$REDIS_TLS_CERT_FILE" "--key" "$REDIS_TLS_KEY_FILE") + [ -n "$REDIS_TLS_CA_FILE" ] && args+=("--cacert" "$REDIS_TLS_CA_FILE") + else + args+=("-p" "$REDIS_PORT") + fi + redis-cli "${args[@]}" "$@" + } + is_master() { + REDIS_ROLE=$(run_redis_command role | head -1) + [[ "$REDIS_ROLE" == "master" ]] + } + + HEADLESS_SERVICE="{{ template "common.names.fullname" . }}-headless.{{- include "common.names.namespace" . }}.svc.{{ .Values.clusterDomain }}" + + get_full_hostname() { + hostname="$1" + + {{- if .Values.useExternalDNS.enabled }} + full_hostname="${hostname}.{{- include "redis.externalDNS.suffix" . }}" + {{- else if eq .Values.sentinel.service.type "NodePort" }} + full_hostname="${hostname}.{{- include "common.names.namespace" . }}" + {{- else }} + full_hostname="${hostname}.${HEADLESS_SERVICE}" + {{- end }} + + {{- if .Values.useHostnames }} + echo "${full_hostname}" + {{- else }} + retry_count=0 + until getent hosts "${full_hostname}" | awk '{ print $1; exit }' | grep .; do + if [[ $retry_count -lt {{ .Values.nameResolutionThreshold }} ]]; then + sleep {{ .Values.nameResolutionTimeout }} + else + error "IP address for ${full_hostname} not found" + exit 1 + fi + ((retry_count++)) + done + {{- end }} + } + + run_sentinel_command() { + if is_boolean_yes "$REDIS_SENTINEL_TLS_ENABLED"; then + {{ .Values.auth.sentinel | ternary "" "env -u REDISCLI_AUTH " -}} redis-cli -h "$REDIS_SERVICE" -p "$REDIS_SENTINEL_TLS_PORT_NUMBER" --tls --cert "$REDIS_SENTINEL_TLS_CERT_FILE" --key "$REDIS_SENTINEL_TLS_KEY_FILE" --cacert "$REDIS_SENTINEL_TLS_CA_FILE" sentinel "$@" + else + {{ .Values.auth.sentinel | ternary "" "env -u REDISCLI_AUTH " -}} redis-cli -h "$REDIS_SERVICE" -p "$REDIS_SENTINEL_PORT" sentinel "$@" + fi + } + sentinel_failover_finished() { + REDIS_SENTINEL_INFO=($(run_sentinel_command get-master-addr-by-name "{{ .Values.sentinel.masterSet }}")) + REDIS_MASTER_HOST="${REDIS_SENTINEL_INFO[0]}" + [[ "$REDIS_MASTER_HOST" != "$(get_full_hostname $HOSTNAME)" ]] + } + + REDIS_SERVICE="{{ include "common.names.fullname" . }}.{{ include "common.names.namespace" . }}.svc.{{ .Values.clusterDomain }}" + + # redis-cli automatically consumes credentials from the REDISCLI_AUTH variable + [[ -n "$REDIS_PASSWORD" ]] && export REDISCLI_AUTH="$REDIS_PASSWORD" + [[ -f "$REDIS_PASSWORD_FILE" ]] && export REDISCLI_AUTH="$(< "${REDIS_PASSWORD_FILE}")" + + + if is_master && ! sentinel_failover_finished; then + echo "I am the master pod and you are stopping me. Pausing client connections." + # Pausing client write connections to avoid data loss + run_redis_command CLIENT PAUSE "{{ mul (add 2 (sub .Values.sentinel.terminationGracePeriodSeconds 10)) 1000 }}" WRITE + + echo "Issuing failover" + # if I am the master, issue a command to failover once + run_sentinel_command failover "{{ .Values.sentinel.masterSet }}" + + {{- if .Values.sentinel.redisShutdownWaitFailover }} + echo "Waiting for sentinel to complete failover for up to {{ sub .Values.sentinel.terminationGracePeriodSeconds 10 }}s" + retry_while "sentinel_failover_finished" "{{ sub .Values.sentinel.terminationGracePeriodSeconds 10 }}" 1 + {{- end }} + else + exit 0 + fi + + {{- if or .Values.sentinel.masterService.enabled .Values.sentinel.service.createMaster }} + push-master-label.sh: | + #!/bin/bash + # https://download.redis.io/redis-stable/sentinel.conf + + echo "${6/.*}" > /etc/shared/current + echo "${4/.*}" > /etc/shared/previous + {{- end }} +{{- else }} + start-master.sh: | + #!/bin/bash + + [[ -f $REDIS_PASSWORD_FILE ]] && export REDIS_PASSWORD="$(< "${REDIS_PASSWORD_FILE}")" + {{- if and .Values.master.containerSecurityContext.runAsUser (eq (.Values.master.containerSecurityContext.runAsUser | int) 0) }} + useradd redis + chown -R redis {{ .Values.master.persistence.path }} + {{- end }} + if [[ -f /opt/bitnami/redis/mounted-etc/master.conf ]];then + cp /opt/bitnami/redis/mounted-etc/master.conf /opt/bitnami/redis/etc/master.conf + fi + if [[ -f /opt/bitnami/redis/mounted-etc/redis.conf ]];then + cp /opt/bitnami/redis/mounted-etc/redis.conf /opt/bitnami/redis/etc/redis.conf + fi + {{- if .Values.tls.enabled }} + ARGS=("--port" "0") + ARGS+=("--tls-port" "${REDIS_TLS_PORT}") + ARGS+=("--tls-cert-file" "${REDIS_TLS_CERT_FILE}") + ARGS+=("--tls-key-file" "${REDIS_TLS_KEY_FILE}") + {{- if not (empty (include "redis.tlsCACert" .)) }} + ARGS+=("--tls-ca-cert-file" "${REDIS_TLS_CA_FILE}") + {{- end }} + ARGS+=("--tls-auth-clients" "${REDIS_TLS_AUTH_CLIENTS}") + {{- if .Values.tls.dhParamsFilename }} + ARGS+=("--tls-dh-params-file" "${REDIS_TLS_DH_PARAMS_FILE}") + {{- end }} + {{- else }} + ARGS=("--port" "${REDIS_PORT}") + {{- end }} + {{- if .Values.auth.enabled }} + ARGS+=("--requirepass" "${REDIS_PASSWORD}") + ARGS+=("--masterauth" "${REDIS_PASSWORD}") + {{- else }} + ARGS+=("--protected-mode" "no") + {{- end }} + ARGS+=("--include" "/opt/bitnami/redis/etc/redis.conf") + ARGS+=("--include" "/opt/bitnami/redis/etc/master.conf") + {{- if .Values.master.extraFlags }} + {{- range .Values.master.extraFlags }} + ARGS+=({{ . | quote }}) + {{- end }} + {{- end }} + {{- if .Values.master.preExecCmds }} + {{- range $command := .Values.master.preExecCmds }} + {{- $command | nindent 4 }} + {{- end }} + {{- end }} + {{- if .Values.master.command }} + exec {{ .Values.master.command }} "${ARGS[@]}" + {{- else }} + exec redis-server "${ARGS[@]}" + {{- end }} + {{- if eq .Values.architecture "replication" }} + start-replica.sh: | + #!/bin/bash + + get_port() { + hostname="$1" + type="$2" + + port_var=$(echo "${hostname^^}_SERVICE_PORT_$type" | sed "s/-/_/g") + port=${!port_var} + + if [ -z "$port" ]; then + case $type in + "SENTINEL") + echo {{ .Values.sentinel.containerPorts.sentinel }} + ;; + "REDIS") + echo {{ .Values.master.containerPorts.redis }} + ;; + esac + else + echo $port + fi + } + + get_full_hostname() { + hostname="$1" + + {{- if .Values.useExternalDNS.enabled }} + full_hostname="${hostname}.{{- include "redis.externalDNS.suffix" . }}" + {{- else if eq .Values.sentinel.service.type "NodePort" }} + full_hostname="${hostname}.{{- include "common.names.namespace" . }}" + {{- else }} + full_hostname="${hostname}.${HEADLESS_SERVICE}" + {{- end }} + + {{- if .Values.useHostnames }} + echo "${full_hostname}" + {{- else }} + retry_count=0 + until getent hosts "${full_hostname}" | awk '{ print $1; exit }' | grep .; do + if [[ $retry_count -lt {{ .Values.nameResolutionThreshold }} ]]; then + sleep {{ .Values.nameResolutionTimeout }} + else + error "IP address for ${full_hostname} not found" + exit 1 + fi + ((retry_count++)) + done + {{- end }} + } + + REDISPORT=$(get_port "$HOSTNAME" "REDIS") + HEADLESS_SERVICE="{{ template "common.names.fullname" . }}-headless.{{ include "common.names.namespace" . }}.svc.{{ .Values.clusterDomain }}" + + [[ -f $REDIS_PASSWORD_FILE ]] && export REDIS_PASSWORD="$(< "${REDIS_PASSWORD_FILE}")" + [[ -f $REDIS_MASTER_PASSWORD_FILE ]] && export REDIS_MASTER_PASSWORD="$(< "${REDIS_MASTER_PASSWORD_FILE}")" + {{- if and .Values.replica.containerSecurityContext.runAsUser (eq (.Values.replica.containerSecurityContext.runAsUser | int) 0) }} + useradd redis + chown -R redis {{ .Values.replica.persistence.path }} + {{- end }} + if [[ -f /opt/bitnami/redis/mounted-etc/replica.conf ]];then + cp /opt/bitnami/redis/mounted-etc/replica.conf /opt/bitnami/redis/etc/replica.conf + fi + if [[ -f /opt/bitnami/redis/mounted-etc/redis.conf ]];then + cp /opt/bitnami/redis/mounted-etc/redis.conf /opt/bitnami/redis/etc/redis.conf + fi + + echo "" >> /opt/bitnami/redis/etc/replica.conf + echo "replica-announce-port $REDISPORT" >> /opt/bitnami/redis/etc/replica.conf + echo "replica-announce-ip $(get_full_hostname "$HOSTNAME")" >> /opt/bitnami/redis/etc/replica.conf + + {{- if .Values.tls.enabled }} + ARGS=("--port" "0") + ARGS+=("--tls-port" "${REDIS_TLS_PORT}") + ARGS+=("--tls-cert-file" "${REDIS_TLS_CERT_FILE}") + ARGS+=("--tls-key-file" "${REDIS_TLS_KEY_FILE}") + {{- if not (empty (include "redis.tlsCACert" .)) }} + ARGS+=("--tls-ca-cert-file" "${REDIS_TLS_CA_FILE}") + {{- end }} + ARGS+=("--tls-auth-clients" "${REDIS_TLS_AUTH_CLIENTS}") + ARGS+=("--tls-replication" "yes") + {{- if .Values.tls.dhParamsFilename }} + ARGS+=("--tls-dh-params-file" "${REDIS_TLS_DH_PARAMS_FILE}") + {{- end }} + {{- else }} + ARGS=("--port" "${REDIS_PORT}") + {{- end }} + ARGS+=("--replicaof" "${REDIS_MASTER_HOST}" "${REDIS_MASTER_PORT_NUMBER}") + {{- if .Values.auth.enabled }} + ARGS+=("--requirepass" "${REDIS_PASSWORD}") + ARGS+=("--masterauth" "${REDIS_MASTER_PASSWORD}") + {{- else }} + ARGS+=("--protected-mode" "no") + {{- end }} + ARGS+=("--include" "/opt/bitnami/redis/etc/redis.conf") + ARGS+=("--include" "/opt/bitnami/redis/etc/replica.conf") + {{- if .Values.replica.extraFlags }} + {{- range .Values.replica.extraFlags }} + ARGS+=({{ . | quote }}) + {{- end }} + {{- end }} + {{- if .Values.replica.preExecCmds }} + {{- range $command := .Values.replica.preExecCmds }} + {{- $command | nindent 4 }} + {{- end }} {{- end }} + {{- if .Values.replica.command }} + exec {{ .Values.replica.command }} "${ARGS[@]}" + {{- else }} + exec redis-server "${ARGS[@]}" + {{- end }} + {{- end }} +{{- end }} +--- +{{- if or .Values.sentinel.masterService.enabled .Values.sentinel.service.createMaster }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ printf "%s-kubectl-scripts" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +data: + update-master-label.sh: | + #!/bin/bash + while true; do + while [ ! -f "/etc/shared/current" ]; do + sleep 1 + done + echo "new master elected, updating label(s)..." + kubectl label pod --field-selector metadata.name="$(< "/etc/shared/current")" isMaster="true" --overwrite + kubectl label pod --field-selector metadata.name="$(< "/etc/shared/current")" app.kubernetes.io/role- + if [ -f /etc/shared/previous ]; then + kubectl label pod --field-selector metadata.name="$(< "/etc/shared/previous")" isMaster="false" --overwrite + fi + rm "/etc/shared/current" "/etc/shared/previous" + done +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/secret-svcbind.yaml b/charts/open-webui/charts/redis/templates/secret-svcbind.yaml new file mode 100644 index 0000000..d3c74ff --- /dev/null +++ b/charts/open-webui/charts/redis/templates/secret-svcbind.yaml @@ -0,0 +1,37 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if .Values.serviceBindings.enabled }} +{{- $host := include "common.names.fullname" . }} +{{- if not .Values.sentinel.enabled }} +{{- $host = printf "%s-master" (include "common.names.fullname" .) }} +{{- end }} +{{- $port := print .Values.master.service.ports.redis }} +{{- if .Values.sentinel.enabled }} +{{- $port = print .Values.sentinel.service.ports.redis }} +{{- end }} +{{- $password := include "redis.password" . }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "common.names.fullname" . }}-svcbind + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: servicebinding.io/redis +data: + provider: {{ print "bitnami" | b64enc | quote }} + type: {{ print "redis" | b64enc | quote }} + host: {{ print $host | b64enc | quote }} + port: {{ print $port | b64enc | quote }} + password: {{ print $password | b64enc | quote }} + {{- if $password }} + uri: {{ printf "redis://:%s@%s:%s" $password $host $port | b64enc | quote }} + {{- else }} + uri: {{ printf "redis://%s:%s" $host $port | b64enc | quote }} + {{- end }} +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/secret.yaml b/charts/open-webui/charts/redis/templates/secret.yaml new file mode 100644 index 0000000..ec69fe2 --- /dev/null +++ b/charts/open-webui/charts/redis/templates/secret.yaml @@ -0,0 +1,25 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if and .Values.auth.enabled (not .Values.auth.existingSecret) (or .Values.auth.usePasswordFileFromSecret (not .Values.auth.usePasswordFiles)) -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if or .Values.secretAnnotations .Values.commonAnnotations }} + annotations: + {{- if .Values.secretAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.secretAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} +type: Opaque +data: + redis-password: {{ include "redis.password" . | b64enc | quote }} +{{- end -}} diff --git a/charts/open-webui/charts/redis/templates/sentinel/hpa.yaml b/charts/open-webui/charts/redis/templates/sentinel/hpa.yaml new file mode 100644 index 0000000..54ec485 --- /dev/null +++ b/charts/open-webui/charts/redis/templates/sentinel/hpa.yaml @@ -0,0 +1,49 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if and .Values.replica.autoscaling.enabled .Values.sentinel.enabled }} +apiVersion: {{ include "common.capabilities.hpa.apiVersion" ( dict "context" $ ) }} +kind: HorizontalPodAutoscaler +metadata: + name: {{ printf "%s-node" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: replica + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + scaleTargetRef: + apiVersion: {{ include "common.capabilities.deployment.apiVersion" . }} + kind: StatefulSet + name: {{ printf "%s-node" (include "common.names.fullname" .) }} + minReplicas: {{ .Values.replica.autoscaling.minReplicas }} + maxReplicas: {{ .Values.replica.autoscaling.maxReplicas }} + metrics: + {{- if .Values.replica.autoscaling.targetMemory }} + - type: Resource + resource: + name: memory + {{- if semverCompare "<1.23-0" (include "common.capabilities.kubeVersion" .) }} + targetAverageUtilization: {{ .Values.replica.autoscaling.targetMemory }} + {{- else }} + target: + type: Utilization + averageUtilization: {{ .Values.replica.autoscaling.targetMemory }} + {{- end }} + {{- end }} + {{- if .Values.replica.autoscaling.targetCPU }} + - type: Resource + resource: + name: cpu + {{- if semverCompare "<1.23-0" (include "common.capabilities.kubeVersion" .) }} + targetAverageUtilization: {{ .Values.replica.autoscaling.targetCPU }} + {{- else }} + target: + type: Utilization + averageUtilization: {{ .Values.replica.autoscaling.targetCPU }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/sentinel/node-services.yaml b/charts/open-webui/charts/redis/templates/sentinel/node-services.yaml new file mode 100644 index 0000000..30ccad5 --- /dev/null +++ b/charts/open-webui/charts/redis/templates/sentinel/node-services.yaml @@ -0,0 +1,67 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if and (eq .Values.architecture "replication") .Values.sentinel.enabled (eq .Values.sentinel.service.type "NodePort") (or .Release.IsUpgrade .Values.sentinel.service.nodePorts.redis ) }} + +{{- range $i := until (int .Values.replica.replicaCount) }} + +{{ $portsmap := (lookup "v1" "ConfigMap" (include "common.names.namespace" $) (printf "%s-%s" ( include "common.names.fullname" $ ) "ports-configmap")).data }} + +{{ $sentinelport := 0}} +{{ $redisport := 0}} +{{- if $portsmap }} +{{ $sentinelport = index $portsmap (printf "%s-node-%s-%s" (include "common.names.fullname" $) (toString $i) "sentinel") }} +{{ $redisport = index $portsmap (printf "%s-node-%s-%s" (include "common.names.fullname" $) (toString $i) "redis") }} +{{- else }} +{{- end }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "common.names.fullname" $ }}-node-{{ $i }} + namespace: {{ include "common.names.namespace" $ | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" $.Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: node + {{- if or $.Values.commonAnnotations $.Values.sentinel.service.annotations }} + {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list $.Values.sentinel.service.annotations $.Values.commonAnnotations ) "context" $ ) }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} + {{- end }} +spec: + type: NodePort + ports: + - name: sentinel + {{- if $.Values.sentinel.service.nodePorts.sentinel }} + nodePort: {{ (add $.Values.sentinel.service.nodePorts.sentinel $i 1) }} + port: {{ (add $.Values.sentinel.service.nodePorts.sentinel $i 1) }} + {{- else }} + nodePort: {{ $sentinelport }} + port: {{ $sentinelport }} + {{- end }} + protocol: TCP + targetPort: {{ $.Values.sentinel.containerPorts.sentinel }} + - name: redis + {{- if $.Values.sentinel.service.nodePorts.redis }} + nodePort: {{ (add $.Values.sentinel.service.nodePorts.redis $i 1) }} + port: {{ (add $.Values.sentinel.service.nodePorts.redis $i 1) }} + {{- else }} + nodePort: {{ $redisport }} + port: {{ $redisport }} + {{- end }} + protocol: TCP + targetPort: {{ $.Values.replica.containerPorts.redis }} + - name: sentinel-internal + nodePort: null + port: {{ $.Values.sentinel.containerPorts.sentinel }} + protocol: TCP + targetPort: {{ $.Values.sentinel.containerPorts.sentinel }} + - name: redis-internal + nodePort: null + port: {{ $.Values.replica.containerPorts.redis }} + protocol: TCP + targetPort: {{ $.Values.replica.containerPorts.redis }} + selector: + statefulset.kubernetes.io/pod-name: {{ template "common.names.fullname" $ }}-node-{{ $i }} +--- +{{- end }} +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/sentinel/pdb.yaml b/charts/open-webui/charts/redis/templates/sentinel/pdb.yaml new file mode 100644 index 0000000..32ddad6 --- /dev/null +++ b/charts/open-webui/charts/redis/templates/sentinel/pdb.yaml @@ -0,0 +1,27 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} +{{- $pdb := coalesce .Values.pdb .Values.replica.pdb }} +{{- if and (eq .Values.architecture "replication") .Values.sentinel.enabled $pdb.create }} +apiVersion: {{ include "common.capabilities.policy.apiVersion" . }} +kind: PodDisruptionBudget +metadata: + name: {{ printf "%s-node" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: node + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + {{- if $pdb.minAvailable }} + minAvailable: {{ $pdb.minAvailable }} + {{- end }} + {{- if or $pdb.maxUnavailable (not $pdb.minAvailable) }} + maxUnavailable: {{ $pdb.maxUnavailable | default 1 }} + {{- end }} + selector: + matchLabels: {{- include "common.labels.matchLabels" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 6 }} + app.kubernetes.io/component: node +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/sentinel/ports-configmap.yaml b/charts/open-webui/charts/redis/templates/sentinel/ports-configmap.yaml new file mode 100644 index 0000000..d55f01a --- /dev/null +++ b/charts/open-webui/charts/redis/templates/sentinel/ports-configmap.yaml @@ -0,0 +1,102 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if and (eq .Values.architecture "replication") .Values.sentinel.enabled (eq .Values.sentinel.service.type "NodePort") (not .Values.sentinel.service.nodePorts.redis ) }} +{{- /* create a list to keep track of ports we choose to use */}} +{{ $chosenports := (list ) }} + +{{- /* Get list of all used nodeports */}} +{{ $usedports := (list ) }} +{{- range $index, $service := (lookup "v1" "Service" "" "").items }} + {{- range.spec.ports }} + {{- if .nodePort }} + {{- $usedports = (append $usedports .nodePort) }} + {{- end }} + {{- end }} +{{- end }} + +{{- /* +comments that start with # are rendered in the output when you debug, so you can less and search for them +Vars in the comment will be rendered out, so you can check their value this way. +https://helm.sh/docs/chart_best_practices/templates/#comments-yaml-comments-vs-template-comments + +remove the template comments and leave the yaml comments to help debug +*/}} + +{{- /* Sort the list */}} +{{ $usedports = $usedports | sortAlpha }} +#usedports {{ $usedports }} + +{{- /* How many nodeports per service do we want to create, except for the main service which is always two */}} +{{ $numberofPortsPerNodeService := 2 }} + +{{- /* for every nodeport we want, loop though the used ports to get an unused port */}} +{{- range $j := until (int (add (mul (int .Values.replica.replicaCount) $numberofPortsPerNodeService) 2)) }} + {{- /* #j={{ $j }} */}} + {{- $nodeport := (add $j 30000) }} + {{- $nodeportfound := false }} + {{- range $i := $usedports }} + {{- /* #i={{ $i }} + #nodeport={{ $nodeport }} + #usedports={{ $usedports }} */}} + {{- if and (has (toString $nodeport) $usedports) (eq $nodeportfound false) }} + {{- /* nodeport conflicts with in use */}} + {{- $nodeport = (add $nodeport 1) }} + {{- else if and ( has $nodeport $chosenports) (eq $nodeportfound false) }} + {{- /* nodeport already chosen, try another */}} + {{- $nodeport = (add $nodeport 1) }} + {{- else if (eq $nodeportfound false) }} + {{- /* nodeport free to use: not already claimed and not in use */}} + {{- /* select nodeport, and place into usedports */}} + {{- $chosenports = (append $chosenports $nodeport) }} + {{- $nodeportfound = true }} + {{- else }} + {{- /* nodeport has already been chosen and locked in, just work through the rest of the list to get to the next nodeport selection */}} + {{- end }} + {{- end }} + {{- if (eq $nodeportfound false) }} + {{- $chosenports = (append $chosenports $nodeport) }} + {{- end }} + +{{- end }} + +{{- /* print the usedports and chosenports for debugging */}} +#usedports {{ $usedports }} +#chosenports {{ $chosenports }}}} + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "common.names.fullname" . }}-ports-configmap + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +data: +{{ $portsmap := (lookup "v1" "ConfigMap" (include "common.names.namespace" .) (printf "%s-%s" ( include "common.names.fullname" . ) "ports-configmap")).data }} +{{- if $portsmap }} +{{- /* configmap already exists, do not install again */ -}} + {{- range $name, $value := $portsmap }} + "{{ $name }}": "{{ $value }}" + {{- end }} +{{- else }} +{{- /* configmap being set for first time */ -}} + {{- range $index, $port := $chosenports }} + {{- $nodenumber := (floor (div $index 2)) }} + {{- if (eq $index 0) }} + "{{ template "common.names.fullname" $ }}-sentinel": "{{ $port }}" + {{- else if (eq $index 1) }} + "{{ template "common.names.fullname" $ }}-redis": "{{ $port }}" + {{- else if (eq (mod $index 2) 0) }} + "{{ template "common.names.fullname" $ }}-node-{{ (sub $nodenumber 1) }}-sentinel": "{{ $port }}" + {{- else if (eq (mod $index 2) 1) }} + "{{ template "common.names.fullname" $ }}-node-{{ (sub $nodenumber 1) }}-redis": "{{ $port }}" + {{- end }} + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/sentinel/service.yaml b/charts/open-webui/charts/redis/templates/sentinel/service.yaml new file mode 100644 index 0000000..9530bde --- /dev/null +++ b/charts/open-webui/charts/redis/templates/sentinel/service.yaml @@ -0,0 +1,160 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if or .Release.IsUpgrade (ne .Values.sentinel.service.type "NodePort") .Values.sentinel.service.nodePorts.redis -}} +{{- if and (eq .Values.architecture "replication") .Values.sentinel.enabled }} +{{ $portsmap := (lookup "v1" "ConfigMap" (include "common.names.namespace" .) (printf "%s-%s" ( include "common.names.fullname" . ) "ports-configmap")).data }} + +{{ $sentinelport := 0}} +{{ $redisport := 0}} +{{- if $portsmap }} +{{ $sentinelport = index $portsmap (printf "%s-%s" (include "common.names.fullname" $) "sentinel") }} +{{ $redisport = index $portsmap (printf "%s-%s" (include "common.names.fullname" $) "redis") }} +{{- else }} +{{- end }} + +apiVersion: v1 +kind: Service +metadata: + name: {{ template "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: node + {{- if or .Values.sentinel.service.annotations .Values.commonAnnotations }} + {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list .Values.sentinel.service.annotations .Values.commonAnnotations ) "context" . ) }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.sentinel.service.type }} + {{- if or (eq .Values.sentinel.service.type "LoadBalancer") (eq .Values.sentinel.service.type "NodePort") }} + externalTrafficPolicy: {{ .Values.sentinel.service.externalTrafficPolicy | quote }} + {{- end }} + {{- if and (eq .Values.sentinel.service.type "LoadBalancer") (not (empty .Values.sentinel.service.loadBalancerIP)) }} + loadBalancerIP: {{ .Values.sentinel.service.loadBalancerIP }} + {{- end }} + {{- if and (eq .Values.sentinel.service.type "LoadBalancer") .Values.sentinel.service.loadBalancerClass }} + loadBalancerClass: {{ .Values.sentinel.service.loadBalancerClass }} + {{- end }} + {{- if and (eq .Values.sentinel.service.type "LoadBalancer") (not (empty .Values.sentinel.service.loadBalancerSourceRanges)) }} + loadBalancerSourceRanges: {{ toYaml .Values.sentinel.service.loadBalancerSourceRanges | nindent 4 }} + {{- end }} + {{- if and .Values.sentinel.service.clusterIP (eq .Values.sentinel.service.type "ClusterIP") }} + clusterIP: {{ .Values.sentinel.service.clusterIP }} + {{- end }} + {{- if .Values.sentinel.service.sessionAffinity }} + sessionAffinity: {{ .Values.sentinel.service.sessionAffinity }} + {{- end }} + {{- if .Values.sentinel.service.sessionAffinityConfig }} + sessionAffinityConfig: {{- include "common.tplvalues.render" (dict "value" .Values.sentinel.service.sessionAffinityConfig "context" $) | nindent 4 }} + {{- end }} + ports: + - name: tcp-redis + {{- if and (or (eq .Values.sentinel.service.type "NodePort") (eq .Values.sentinel.service.type "LoadBalancer")) .Values.sentinel.service.nodePorts.redis }} + port: {{ .Values.sentinel.service.nodePorts.redis }} + {{- else if eq .Values.sentinel.service.type "NodePort" }} + port: {{ $redisport }} + {{- else}} + port: {{ .Values.sentinel.service.ports.redis }} + {{- end }} + targetPort: {{ .Values.replica.containerPorts.redis }} + {{- if and (or (eq .Values.sentinel.service.type "NodePort") (eq .Values.sentinel.service.type "LoadBalancer")) .Values.sentinel.service.nodePorts.redis }} + nodePort: {{ .Values.sentinel.service.nodePorts.redis }} + {{- else if eq .Values.sentinel.service.type "ClusterIP" }} + nodePort: null + {{- else if eq .Values.sentinel.service.type "NodePort" }} + nodePort: {{ $redisport }} + {{- end }} + - name: tcp-sentinel + {{- if and (or (eq .Values.sentinel.service.type "NodePort") (eq .Values.sentinel.service.type "LoadBalancer")) .Values.sentinel.service.nodePorts.sentinel }} + port: {{ .Values.sentinel.service.nodePorts.sentinel }} + {{- else if eq .Values.sentinel.service.type "NodePort" }} + port: {{ $sentinelport }} + {{- else }} + port: {{ .Values.sentinel.service.ports.sentinel }} + {{- end }} + targetPort: {{ .Values.sentinel.containerPorts.sentinel }} + {{- if and (or (eq .Values.sentinel.service.type "NodePort") (eq .Values.sentinel.service.type "LoadBalancer")) .Values.sentinel.service.nodePorts.sentinel }} + nodePort: {{ .Values.sentinel.service.nodePorts.sentinel }} + {{- else if eq .Values.sentinel.service.type "ClusterIP" }} + nodePort: null + {{- else if eq .Values.sentinel.service.type "NodePort" }} + nodePort: {{ $sentinelport }} + {{- end }} + {{- if eq .Values.sentinel.service.type "NodePort" }} + - name: sentinel-internal + nodePort: null + port: {{ .Values.sentinel.containerPorts.sentinel }} + protocol: TCP + targetPort: {{ .Values.sentinel.containerPorts.sentinel }} + - name: redis-internal + nodePort: null + port: {{ .Values.replica.containerPorts.redis }} + protocol: TCP + targetPort: {{ .Values.replica.containerPorts.redis }} + {{- end }} + {{- if .Values.sentinel.service.extraPorts }} + {{- include "common.tplvalues.render" (dict "value" .Values.sentinel.service.extraPorts "context" $) | nindent 4 }} + {{- end }} + {{- $podLabels := include "common.tplvalues.merge" ( dict "values" ( list .Values.replica.podLabels .Values.commonLabels ) "context" . ) }} + selector: {{- include "common.labels.matchLabels" ( dict "customLabels" $podLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: node + +{{- $masterServiceConfig := ternary .Values.sentinel.masterService .Values.sentinel.service .Values.sentinel.masterService.enabled -}} +{{- if and .Values.sentinel.enabled (or .Values.sentinel.masterService.enabled .Values.sentinel.service.createMaster) }} +--- +apiVersion: v1 +kind: Service +metadata: + name: "{{ template "common.names.fullname" . }}-master" + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: node + {{- if or $masterServiceConfig.annotations .Values.commonAnnotations }} + {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list ($masterServiceConfig.annotations) .Values.commonAnnotations ) "context" . ) }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} + {{- end }} +spec: + type: {{ $masterServiceConfig.type }} + {{- if or (eq $masterServiceConfig.type "LoadBalancer") (eq $masterServiceConfig.type "NodePort") }} + externalTrafficPolicy: {{ $masterServiceConfig.externalTrafficPolicy | quote }} + {{- end }} + {{- if and (eq $masterServiceConfig.type "LoadBalancer") (not (empty ($masterServiceConfig.loadBalancerIP))) }} + loadBalancerIP: {{ $masterServiceConfig.loadBalancerIP }} + {{- end }} + {{- if and (eq $masterServiceConfig.type "LoadBalancer") (not (empty ($masterServiceConfig.loadBalancerClass))) }} + loadBalancerClass: {{ $masterServiceConfig.loadBalancerClass }} + {{- end }} + {{- if and (eq $masterServiceConfig.type "LoadBalancer") (not (empty ($masterServiceConfig.loadBalancerSourceRanges))) }} + loadBalancerSourceRanges: {{ toYaml ($masterServiceConfig.loadBalancerSourceRanges) | nindent 4 }} + {{- end }} + {{- if and (eq $masterServiceConfig.type "ClusterIP") (not (empty ($masterServiceConfig.clusterIP))) }} + clusterIP: {{ $masterServiceConfig.clusterIP }} + {{- end }} + sessionAffinity: {{ $masterServiceConfig.sessionAffinity }} + {{- if $masterServiceConfig.sessionAffinityConfig }} + sessionAffinityConfig: {{- include "common.tplvalues.render" (dict "value" ($masterServiceConfig.sessionAffinityConfig) "context" $) | nindent 4 }} + {{- end }} + ports: + - name: tcp-redis + {{- if and (or (eq $masterServiceConfig.type "NodePort") (eq $masterServiceConfig.type "LoadBalancer")) ($masterServiceConfig.nodePorts.redis) }} + port: {{ $masterServiceConfig.nodePorts.redis }} + {{- else if eq $masterServiceConfig.type "NodePort" }} + port: {{ $redisport }} + {{- else }} + port: {{ $masterServiceConfig.ports.redis }} + {{- end }} + targetPort: {{ .Values.replica.containerPorts.redis }} + {{- if and (or (eq $masterServiceConfig.type "NodePort") (eq $masterServiceConfig.type "LoadBalancer")) ($masterServiceConfig.nodePorts.redis) }} + nodePort: {{ $masterServiceConfig.nodePorts.redis }} + {{- else if eq $masterServiceConfig.type "ClusterIP" }} + nodePort: null + {{- else if eq $masterServiceConfig.type "NodePort" }} + nodePort: {{ $redisport }} + {{- end }} + selector: {{- include "common.labels.matchLabels" ( dict "customLabels" $podLabels "context" $ ) | nindent 4 }} + isMaster: "true" +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/sentinel/statefulset.yaml b/charts/open-webui/charts/redis/templates/sentinel/statefulset.yaml new file mode 100644 index 0000000..24baec3 --- /dev/null +++ b/charts/open-webui/charts/redis/templates/sentinel/statefulset.yaml @@ -0,0 +1,851 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if or .Release.IsUpgrade (ne .Values.sentinel.service.type "NodePort") .Values.sentinel.service.nodePorts.redis -}} +{{- if and (eq .Values.architecture "replication") .Values.sentinel.enabled }} +apiVersion: {{ include "common.capabilities.statefulset.apiVersion" . }} +kind: StatefulSet +metadata: + name: {{ printf "%s-node" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + app.kubernetes.io/component: node + {{- if or .Values.commonAnnotations .Values.sentinel.annotations }} + {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list .Values.sentinel.annotations .Values.commonAnnotations ) "context" . ) }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.replica.replicaCount }} + revisionHistoryLimit: {{ .Values.replica.revisionHistoryLimit }} + {{- $podLabels := include "common.tplvalues.merge" ( dict "values" ( list .Values.replica.podLabels .Values.commonLabels ) "context" . ) }} + selector: + matchLabels: {{- include "common.labels.matchLabels" ( dict "customLabels" $podLabels "context" $ ) | nindent 6 }} + app.kubernetes.io/component: node + serviceName: {{ printf "%s-headless" (include "common.names.fullname" .) }} + {{- if .Values.replica.updateStrategy }} + updateStrategy: {{- toYaml .Values.replica.updateStrategy | nindent 4 }} + {{- end }} + {{- if and .Values.replica.minReadySeconds (semverCompare ">= 1.23-0" (include "common.capabilities.kubeVersion" .)) }} + minReadySeconds: {{ .Values.replica.minReadySeconds }} + {{- end }} + {{- if .Values.replica.podManagementPolicy }} + podManagementPolicy: {{ .Values.replica.podManagementPolicy | quote }} + {{- end }} + template: + metadata: + labels: {{- include "common.labels.standard" ( dict "customLabels" $podLabels "context" $ ) | nindent 8 }} + app.kubernetes.io/component: node + {{- if .Values.sentinel.masterService.enabled }} + app.kubernetes.io/role: slave + {{- end }} + {{- if and .Values.metrics.enabled .Values.metrics.podLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.podLabels "context" $ ) | nindent 8 }} + {{- end }} + annotations: + {{- if (include "redis.createConfigmap" .) }} + checksum/configmap: {{ pick ( include (print $.Template.BasePath "/configmap.yaml") . | fromYaml ) "data" | toYaml | sha256sum }} + {{- end }} + checksum/health: {{ pick ( include (print $.Template.BasePath "/health-configmap.yaml") . | fromYaml ) "data" | toYaml | sha256sum }} + checksum/scripts: {{ pick ( include (print $.Template.BasePath "/scripts-configmap.yaml") . | fromYaml ) "data" | toYaml | sha256sum }} + checksum/secret: {{ pick ( include (print $.Template.BasePath "/secret.yaml") . | fromYaml ) "data" | toYaml | sha256sum }} + {{- if .Values.replica.podAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.replica.podAnnotations "context" $ ) | nindent 8 }} + {{- end }} + {{- if and .Values.metrics.enabled .Values.metrics.podAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.podAnnotations "context" $ ) | nindent 8 }} + {{- end }} + spec: + {{- if .Values.sentinel.extraPodSpec }} + {{- include "common.tplvalues.render" (dict "value" .Values.sentinel.extraPodSpec "context" $) | nindent 6 }} + {{- end }} + {{- include "redis.imagePullSecrets" . | nindent 6 }} + automountServiceAccountToken: {{ .Values.replica.automountServiceAccountToken }} + {{- if .Values.replica.hostAliases }} + hostAliases: {{- include "common.tplvalues.render" (dict "value" .Values.replica.hostAliases "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.replica.podSecurityContext.enabled }} + securityContext: {{- include "common.compatibility.renderSecurityContext" (dict "secContext" .Values.replica.podSecurityContext "context" $) | nindent 8 }} + {{- end }} + serviceAccountName: {{ template "redis.serviceAccountName" . }} + {{- if .Values.replica.priorityClassName }} + priorityClassName: {{ .Values.replica.priorityClassName | quote }} + {{- end }} + {{- if .Values.replica.affinity }} + affinity: {{- include "common.tplvalues.render" (dict "value" .Values.replica.affinity "context" $) | nindent 8 }} + {{- else }} + affinity: + podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.replica.podAffinityPreset "component" "node" "customLabels" $podLabels "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.replica.podAntiAffinityPreset "component" "node" "customLabels" $podLabels "context" $) | nindent 10 }} + nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.replica.nodeAffinityPreset.type "key" .Values.replica.nodeAffinityPreset.key "values" .Values.replica.nodeAffinityPreset.values) | nindent 10 }} + {{- end }} + {{- if .Values.replica.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.replica.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.replica.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.replica.tolerations "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.replica.topologySpreadConstraints }} + topologySpreadConstraints: {{- include "common.tplvalues.render" (dict "value" .Values.replica.topologySpreadConstraints "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.replica.shareProcessNamespace }} + shareProcessNamespace: {{ .Values.replica.shareProcessNamespace }} + {{- end }} + {{- if .Values.replica.schedulerName }} + schedulerName: {{ .Values.replica.schedulerName | quote }} + {{- end }} + {{- if .Values.replica.dnsPolicy }} + dnsPolicy: {{ .Values.replica.dnsPolicy }} + {{- end }} + {{- if .Values.replica.dnsConfig }} + dnsConfig: {{- include "common.tplvalues.render" (dict "value" .Values.replica.dnsConfig "context" $) | nindent 8 }} + {{- end }} + enableServiceLinks: {{ .Values.sentinel.enableServiceLinks }} + terminationGracePeriodSeconds: {{ .Values.sentinel.terminationGracePeriodSeconds }} + containers: + - name: redis + image: {{ template "redis.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.replica.lifecycleHooks }} + lifecycle: {{- include "common.tplvalues.render" (dict "value" .Values.replica.lifecycleHooks "context" $) | nindent 12 }} + {{- else }} + lifecycle: + preStop: + exec: + command: + - /bin/bash + - -c + - /opt/bitnami/scripts/start-scripts/prestop-redis.sh + {{- end }} + {{- end }} + {{- if .Values.replica.containerSecurityContext.enabled }} + securityContext: {{- include "common.compatibility.renderSecurityContext" (dict "secContext" .Values.replica.containerSecurityContext "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} + {{- else if .Values.replica.command }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.replica.command "context" $) | nindent 12 }} + {{- else }} + command: + - /bin/bash + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} + {{- else if .Values.replica.args }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.replica.args "context" $) | nindent 12 }} + {{- else }} + args: + - -c + - /opt/bitnami/scripts/start-scripts/start-node.sh + {{- end }} + env: + - name: BITNAMI_DEBUG + value: {{ ternary "true" "false" (or .Values.image.debug .Values.diagnosticMode.enabled) | quote }} + - name: REDIS_MASTER_PORT_NUMBER + value: {{ .Values.replica.containerPorts.redis | quote }} + - name: ALLOW_EMPTY_PASSWORD + value: {{ ternary "no" "yes" .Values.auth.enabled | quote }} + {{- if .Values.auth.enabled }} + {{- if .Values.auth.usePasswordFiles }} + - name: REDIS_PASSWORD_FILE + value: "/opt/bitnami/redis/secrets/redis-password" + - name: REDIS_MASTER_PASSWORD_FILE + value: "/opt/bitnami/redis/secrets/redis-password" + {{- else }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "redis.secretName" . }} + key: {{ template "redis.secretPasswordKey" . }} + - name: REDIS_MASTER_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "redis.secretName" . }} + key: {{ template "redis.secretPasswordKey" . }} + {{- end }} + {{- end }} + - name: REDIS_TLS_ENABLED + value: {{ ternary "yes" "no" .Values.tls.enabled | quote }} + {{- if .Values.tls.enabled }} + - name: REDIS_TLS_PORT + value: {{ .Values.replica.containerPorts.redis | quote }} + - name: REDIS_TLS_AUTH_CLIENTS + value: {{ ternary "yes" "no" .Values.tls.authClients | quote }} + - name: REDIS_TLS_CERT_FILE + value: {{ template "redis.tlsCert" . }} + - name: REDIS_TLS_KEY_FILE + value: {{ template "redis.tlsCertKey" . }} + {{- if not (empty (include "redis.tlsCACert" .)) }} + - name: REDIS_TLS_CA_FILE + value: {{ template "redis.tlsCACert" . }} + {{- end }} + {{- if .Values.tls.dhParamsFilename }} + - name: REDIS_TLS_DH_PARAMS_FILE + value: {{ template "redis.tlsDHParams" . }} + {{- end }} + {{- else }} + - name: REDIS_PORT + value: {{ .Values.replica.containerPorts.redis | quote }} + {{- end }} + - name: REDIS_SENTINEL_TLS_ENABLED + value: {{ ternary "yes" "no" .Values.tls.enabled | quote }} + {{- if .Values.tls.enabled }} + - name: REDIS_SENTINEL_TLS_PORT_NUMBER + value: {{ .Values.sentinel.containerPorts.sentinel | quote }} + - name: REDIS_SENTINEL_TLS_AUTH_CLIENTS + value: {{ ternary "yes" "no" .Values.tls.authClients | quote }} + - name: REDIS_SENTINEL_TLS_CERT_FILE + value: {{ template "redis.tlsCert" . }} + - name: REDIS_SENTINEL_TLS_KEY_FILE + value: {{ template "redis.tlsCertKey" . }} + {{- if not (empty (include "redis.tlsCACert" .)) }} + - name: REDIS_SENTINEL_TLS_CA_FILE + value: {{ template "redis.tlsCACert" . }} + {{- end }} + {{- if .Values.tls.dhParamsFilename }} + - name: REDIS_SENTINEL_TLS_DH_PARAMS_FILE + value: {{ template "redis.tlsDHParams" . }} + {{- end }} + {{- else }} + - name: REDIS_SENTINEL_PORT + value: {{ .Values.sentinel.containerPorts.sentinel | quote }} + {{- end }} + - name: REDIS_DATA_DIR + value: {{ .Values.replica.persistence.path }} + {{- if .Values.replica.externalMaster.enabled }} + - name: REDIS_EXTERNAL_MASTER_HOST + value: {{ .Values.replica.externalMaster.host | quote }} + - name: REDIS_EXTERNAL_MASTER_PORT + value: {{ .Values.replica.externalMaster.port | quote }} + {{- end }} + {{- if .Values.replica.extraEnvVars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.replica.extraEnvVars "context" $ ) | nindent 12 }} + {{- end }} + {{- if or .Values.replica.extraEnvVarsCM .Values.replica.extraEnvVarsSecret }} + envFrom: + {{- if .Values.replica.extraEnvVarsCM }} + - configMapRef: + name: {{ .Values.replica.extraEnvVarsCM }} + {{- end }} + {{- if .Values.replica.extraEnvVarsSecret }} + - secretRef: + name: {{ .Values.replica.extraEnvVarsSecret }} + {{- end }} + {{- end }} + ports: + - name: redis + containerPort: {{ .Values.replica.containerPorts.redis }} + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.replica.customStartupProbe }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.replica.customStartupProbe "context" $) | nindent 12 }} + {{- else if .Values.replica.startupProbe.enabled }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.replica.startupProbe "enabled") "context" $) | nindent 12 }} + exec: + command: + - sh + - -c + - /health/ping_liveness_local.sh {{ .Values.replica.livenessProbe.timeoutSeconds }} + {{- end }} + {{- if .Values.replica.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.replica.customLivenessProbe "context" $) | nindent 12 }} + {{- else if .Values.replica.livenessProbe.enabled }} + livenessProbe: + initialDelaySeconds: {{ .Values.replica.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.replica.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.replica.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.replica.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.replica.livenessProbe.failureThreshold }} + exec: + command: + - sh + - -c + - /health/ping_liveness_local.sh {{ .Values.replica.livenessProbe.timeoutSeconds }} + {{- end }} + {{- if .Values.replica.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.replica.customReadinessProbe "context" $) | nindent 12 }} + {{- else if .Values.replica.readinessProbe.enabled }} + readinessProbe: + initialDelaySeconds: {{ .Values.replica.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.replica.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.replica.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.replica.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.replica.readinessProbe.failureThreshold }} + exec: + command: + - sh + - -c + - /health/ping_readiness_local.sh {{ .Values.replica.readinessProbe.timeoutSeconds }} + {{- end }} + {{- end }} + {{- if .Values.replica.resources }} + resources: {{- toYaml .Values.replica.resources | nindent 12 }} + {{- else if ne .Values.replica.resourcesPreset "none" }} + resources: {{- include "common.resources.preset" (dict "type" .Values.replica.resourcesPreset) | nindent 12 }} + {{- end }} + volumeMounts: + - name: start-scripts + mountPath: /opt/bitnami/scripts/start-scripts + - name: health + mountPath: /health + - name: sentinel-data + mountPath: /opt/bitnami/redis-sentinel/etc + {{- if .Values.auth.usePasswordFiles }} + - name: redis-password + mountPath: /opt/bitnami/redis/secrets/ + {{- end }} + - name: redis-data + mountPath: {{ .Values.replica.persistence.path }} + {{- if .Values.replica.persistence.subPath }} + subPath: {{ .Values.replica.persistence.subPath }} + {{- else if .Values.replica.persistence.subPathExpr }} + subPathExpr: {{ .Values.replica.persistence.subPathExpr }} + {{- end }} + - name: config + mountPath: /opt/bitnami/redis/mounted-etc + - name: empty-dir + mountPath: /opt/bitnami/redis/etc + subPath: app-conf-dir + - name: empty-dir + mountPath: /tmp + subPath: tmp-dir + {{- if .Values.tls.enabled }} + - name: redis-certificates + mountPath: /opt/bitnami/redis/certs + readOnly: true + {{- end }} + {{- if .Values.replica.extraVolumeMounts }} + {{- include "common.tplvalues.render" ( dict "value" .Values.replica.extraVolumeMounts "context" $ ) | nindent 12 }} + {{- end }} + - name: sentinel + image: {{ template "redis.sentinel.image" . }} + imagePullPolicy: {{ .Values.sentinel.image.pullPolicy | quote }} + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.sentinel.lifecycleHooks }} + lifecycle: {{- include "common.tplvalues.render" (dict "value" .Values.sentinel.lifecycleHooks "context" $) | nindent 12 }} + {{- else }} + lifecycle: + preStop: + exec: + command: + - /bin/bash + - -c + - /opt/bitnami/scripts/start-scripts/prestop-sentinel.sh + {{- end }} + {{- end }} + {{- if .Values.sentinel.containerSecurityContext.enabled }} + securityContext: {{- include "common.compatibility.renderSecurityContext" (dict "secContext" .Values.sentinel.containerSecurityContext "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} + {{- else if .Values.sentinel.command }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.sentinel.command "context" $) | nindent 12 }} + {{- else }} + command: + - /bin/bash + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} + {{- else if .Values.sentinel.args }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.sentinel.args "context" $) | nindent 12 }} + {{- else }} + args: + - -c + - /opt/bitnami/scripts/start-scripts/start-sentinel.sh + {{- end }} + env: + - name: BITNAMI_DEBUG + value: {{ ternary "true" "false" (or .Values.sentinel.image.debug .Values.diagnosticMode.enabled) | quote }} + {{- if .Values.auth.enabled }} + {{- if .Values.auth.usePasswordFiles }} + - name: REDIS_PASSWORD_FILE + value: "/opt/bitnami/redis/secrets/redis-password" + {{- else }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "redis.secretName" . }} + key: {{ template "redis.secretPasswordKey" . }} + {{- end }} + {{- else }} + - name: ALLOW_EMPTY_PASSWORD + value: "yes" + {{- end }} + - name: REDIS_SENTINEL_TLS_ENABLED + value: {{ ternary "yes" "no" .Values.tls.enabled | quote }} + {{- if .Values.tls.enabled }} + - name: REDIS_SENTINEL_TLS_PORT_NUMBER + value: {{ .Values.sentinel.containerPorts.sentinel | quote }} + - name: REDIS_SENTINEL_TLS_AUTH_CLIENTS + value: {{ ternary "yes" "no" .Values.tls.authClients | quote }} + - name: REDIS_SENTINEL_TLS_CERT_FILE + value: {{ template "redis.tlsCert" . }} + - name: REDIS_SENTINEL_TLS_KEY_FILE + value: {{ template "redis.tlsCertKey" . }} + {{- if not (empty (include "redis.tlsCACert" .)) }} + - name: REDIS_SENTINEL_TLS_CA_FILE + value: {{ template "redis.tlsCACert" . }} + {{- end }} + {{- if .Values.tls.dhParamsFilename }} + - name: REDIS_SENTINEL_TLS_DH_PARAMS_FILE + value: {{ template "redis.tlsDHParams" . }} + {{- end }} + {{- else }} + - name: REDIS_SENTINEL_PORT + value: {{ .Values.sentinel.containerPorts.sentinel | quote }} + {{- end }} + {{- if .Values.sentinel.externalMaster.enabled }} + - name: REDIS_EXTERNAL_MASTER_HOST + value: {{ .Values.sentinel.externalMaster.host | quote }} + - name: REDIS_EXTERNAL_MASTER_PORT + value: {{ .Values.sentinel.externalMaster.port | quote }} + {{- end }} + {{- if .Values.sentinel.extraEnvVars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.sentinel.extraEnvVars "context" $ ) | nindent 12 }} + {{- end }} + {{- if or .Values.sentinel.extraEnvVarsCM .Values.sentinel.extraEnvVarsSecret }} + envFrom: + {{- if .Values.sentinel.extraEnvVarsCM }} + - configMapRef: + name: {{ .Values.sentinel.extraEnvVarsCM }} + {{- end }} + {{- if .Values.sentinel.extraEnvVarsSecret }} + - secretRef: + name: {{ .Values.sentinel.extraEnvVarsSecret }} + {{- end }} + {{- end }} + ports: + - name: redis-sentinel + containerPort: {{ .Values.sentinel.containerPorts.sentinel }} + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.sentinel.customStartupProbe }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.sentinel.customStartupProbe "context" $) | nindent 12 }} + {{- else if .Values.sentinel.startupProbe.enabled }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.sentinel.startupProbe "enabled") "context" $) | nindent 12 }} + exec: + command: + - sh + - -c + - /health/ping_sentinel.sh {{ .Values.sentinel.livenessProbe.timeoutSeconds }} + {{- end }} + {{- if .Values.sentinel.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.sentinel.customLivenessProbe "context" $) | nindent 12 }} + {{- else if .Values.sentinel.livenessProbe.enabled }} + livenessProbe: + initialDelaySeconds: {{ .Values.sentinel.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.sentinel.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.sentinel.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.sentinel.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.sentinel.livenessProbe.failureThreshold }} + exec: + command: + - sh + - -c + - /health/ping_sentinel.sh {{ .Values.sentinel.livenessProbe.timeoutSeconds }} + {{- end }} + {{- end }} + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.sentinel.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.sentinel.customReadinessProbe "context" $) | nindent 12 }} + {{- else if .Values.sentinel.readinessProbe.enabled }} + readinessProbe: + initialDelaySeconds: {{ .Values.sentinel.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.sentinel.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.sentinel.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.sentinel.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.sentinel.readinessProbe.failureThreshold }} + exec: + command: + - sh + - -c + - /health/ping_sentinel.sh {{ .Values.sentinel.readinessProbe.timeoutSeconds }} + {{- end }} + {{- end }} + {{- if .Values.sentinel.resources }} + resources: {{- toYaml .Values.sentinel.resources | nindent 12 }} + {{- else if ne .Values.sentinel.resourcesPreset "none" }} + resources: {{- include "common.resources.preset" (dict "type" .Values.sentinel.resourcesPreset) | nindent 12 }} + {{- end }} + volumeMounts: + - name: empty-dir + mountPath: /tmp + subPath: tmp-dir + - name: start-scripts + mountPath: /opt/bitnami/scripts/start-scripts + - name: health + mountPath: /health + {{- if or .Values.sentinel.masterService.enabled .Values.sentinel.service.createMaster}} + - name: kubectl-shared + mountPath: /etc/shared + {{- end }} + - name: sentinel-data + mountPath: /opt/bitnami/redis-sentinel/etc + {{- if .Values.auth.usePasswordFiles }} + - name: redis-password + mountPath: /opt/bitnami/redis/secrets/ + {{- end }} + - name: redis-data + mountPath: {{ .Values.replica.persistence.path }} + {{- if .Values.replica.persistence.subPath }} + subPath: {{ .Values.replica.persistence.subPath }} + {{- else if .Values.replica.persistence.subPathExpr }} + subPathExpr: {{ .Values.replica.persistence.subPathExpr }} + {{- end }} + - name: config + mountPath: /opt/bitnami/redis-sentinel/mounted-etc + {{- if .Values.tls.enabled }} + - name: redis-certificates + mountPath: /opt/bitnami/redis/certs + readOnly: true + {{- end }} + {{- if .Values.sentinel.extraVolumeMounts }} + {{- include "common.tplvalues.render" ( dict "value" .Values.sentinel.extraVolumeMounts "context" $ ) | nindent 12 }} + {{- end }} + {{- if .Values.metrics.enabled }} + - name: metrics + image: {{ template "redis.metrics.image" . }} + imagePullPolicy: {{ .Values.metrics.image.pullPolicy | quote }} + {{- if .Values.metrics.containerSecurityContext.enabled }} + securityContext: {{- include "common.compatibility.renderSecurityContext" (dict "secContext" .Values.metrics.containerSecurityContext "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} + {{- else }} + command: + - /bin/bash + - -c + - | + if [[ -f '/secrets/redis-password' ]]; then + export REDIS_PASSWORD=$(cat /secrets/redis-password) + fi + redis_exporter{{- range $key, $value := .Values.metrics.extraArgs }} --{{ $key }}={{ $value }}{{- end }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} + {{- end }} + env: + - name: REDIS_ALIAS + value: {{ template "common.names.fullname" . }} + - name: REDIS_EXPORTER_WEB_LISTEN_ADDRESS + value: {{ printf ":%v" .Values.metrics.containerPorts.http }} + {{- if .Values.auth.enabled }} + - name: REDIS_USER + value: default + {{- if (not .Values.auth.usePasswordFiles) }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "redis.secretName" . }} + key: {{ template "redis.secretPasswordKey" . }} + {{- end }} + {{- end }} + {{- if .Values.tls.enabled }} + - name: REDIS_ADDR + value: rediss://{{ .Values.metrics.redisTargetHost }}:{{ .Values.replica.containerPorts.redis }} + {{- if .Values.tls.authClients }} + - name: REDIS_EXPORTER_TLS_CLIENT_KEY_FILE + value: {{ template "redis.tlsCertKey" . }} + - name: REDIS_EXPORTER_TLS_CLIENT_CERT_FILE + value: {{ template "redis.tlsCert" . }} + {{- end }} + {{- if not (empty (include "redis.tlsCACert" .)) }} + - name: REDIS_EXPORTER_TLS_CA_CERT_FILE + value: {{ template "redis.tlsCACert" . }} + {{- end }} + {{- end }} + {{- if .Values.metrics.extraEnvVars }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.extraEnvVars "context" $) | nindent 12 }} + {{- end }} + ports: + - name: metrics + containerPort: {{ .Values.metrics.containerPorts.http }} + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.metrics.customStartupProbe }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.customStartupProbe "context" $) | nindent 12 }} + {{- else if .Values.metrics.startupProbe.enabled }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.metrics.startupProbe "enabled") "context" $) | nindent 12 }} + tcpSocket: + port: metrics + {{- end }} + {{- if .Values.metrics.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.customLivenessProbe "context" $) | nindent 12 }} + {{- else if .Values.metrics.livenessProbe.enabled }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.metrics.livenessProbe "enabled") "context" $) | nindent 12 }} + tcpSocket: + port: metrics + {{- end }} + {{- if .Values.metrics.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.metrics.customReadinessProbe "context" $) | nindent 12 }} + {{- else if .Values.metrics.readinessProbe.enabled }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.metrics.readinessProbe "enabled") "context" $) | nindent 12 }} + httpGet: + path: / + port: metrics + {{- end }} + {{- end }} + {{- if .Values.metrics.resources }} + resources: {{- toYaml .Values.metrics.resources | nindent 12 }} + {{- else if ne .Values.metrics.resourcesPreset "none" }} + resources: {{- include "common.resources.preset" (dict "type" .Values.metrics.resourcesPreset) | nindent 12 }} + {{- end }} + volumeMounts: + - name: empty-dir + mountPath: /tmp + subPath: tmp-dir + {{- if .Values.auth.usePasswordFiles }} + - name: redis-password + mountPath: /secrets/ + {{- end }} + {{- if .Values.tls.enabled }} + - name: redis-certificates + mountPath: /opt/bitnami/redis/certs + readOnly: true + {{- end }} + {{- if .Values.metrics.extraVolumeMounts }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.extraVolumeMounts "context" $ ) | nindent 12 }} + {{- end }} + {{- end }} + {{- if or .Values.sentinel.masterService.enabled .Values.sentinel.service.createMaster }} + - name: kubectl-shared + image: {{ template "redis.kubectl.image" . }} + imagePullPolicy: {{ .Values.kubectl.image.pullPolicy | quote }} + command: {{- toYaml .Values.kubectl.command | nindent 12 }} + {{- if .Values.kubectl.containerSecurityContext.enabled }} + securityContext: {{- include "common.compatibility.renderSecurityContext" (dict "secContext" .Values.kubectl.containerSecurityContext "context" $) | nindent 12 }} + {{- end }} + volumeMounts: + - name: kubectl-shared + mountPath: /etc/shared + - name: kubectl-scripts + mountPath: /opt/bitnami/scripts/kubectl-scripts + {{- if .Values.kubectl.resources }} + resources: {{- toYaml .Values.kubectl.resources | nindent 12 }} + {{- end }} + {{- end }} + {{- if .Values.replica.sidecars }} + {{- include "common.tplvalues.render" (dict "value" .Values.replica.sidecars "context" $) | nindent 8 }} + {{- end }} + {{- $needsVolumePermissions := and .Values.volumePermissions.enabled .Values.replica.persistence.enabled .Values.replica.podSecurityContext.enabled .Values.replica.containerSecurityContext.enabled }} + {{- if or .Values.replica.initContainers $needsVolumePermissions .Values.sysctl.enabled }} + initContainers: + {{- if .Values.replica.initContainers }} + {{- include "common.tplvalues.render" (dict "value" .Values.replica.initContainers "context" $) | nindent 8 }} + {{- end }} + {{- if $needsVolumePermissions }} + - name: volume-permissions + image: {{ include "redis.volumePermissions.image" . }} + imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | quote }} + command: + - /bin/bash + - -ec + - | + {{- if eq ( toString ( .Values.volumePermissions.containerSecurityContext.runAsUser )) "auto" }} + chown -R `id -u`:`id -G | cut -d " " -f2` {{ .Values.replica.persistence.path }} + {{- else }} + chown -R {{ .Values.replica.containerSecurityContext.runAsUser }}:{{ .Values.replica.podSecurityContext.fsGroup }} {{ .Values.replica.persistence.path }} + {{- end }} + {{- if eq ( toString ( .Values.volumePermissions.containerSecurityContext.runAsUser )) "auto" }} + securityContext: {{- omit .Values.volumePermissions.containerSecurityContext "runAsUser" | toYaml | nindent 12 }} + {{- else }} + securityContext: {{- .Values.volumePermissions.containerSecurityContext | toYaml | nindent 12 }} + {{- end }} + {{- if .Values.volumePermissions.extraEnvVars }} + env: + {{- include "common.tplvalues.render" (dict "value" .Values.volumePermissions.extraEnvVars "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.volumePermissions.resources }} + resources: {{- toYaml .Values.volumePermissions.resources | nindent 12 }} + {{- else if ne .Values.volumePermissions.resourcesPreset "none" }} + resources: {{- include "common.resources.preset" (dict "type" .Values.volumePermissions.resourcesPreset) | nindent 12 }} + {{- end }} + volumeMounts: + - name: empty-dir + mountPath: /tmp + subPath: tmp-dir + - name: redis-data + mountPath: {{ .Values.replica.persistence.path }} + {{- if .Values.replica.persistence.subPath }} + subPath: {{ .Values.replica.persistence.subPath }} + {{- else if .Values.replica.persistence.subPathExpr }} + subPathExpr: {{ .Values.replica.persistence.subPathExpr }} + {{- end }} + {{- end }} + {{- if .Values.sysctl.enabled }} + - name: init-sysctl + image: {{ include "redis.sysctl.image" . }} + imagePullPolicy: {{ default "" .Values.sysctl.image.pullPolicy | quote }} + securityContext: + privileged: true + runAsUser: 0 + {{- if .Values.sysctl.command }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.sysctl.command "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.sysctl.resources }} + resources: {{- toYaml .Values.sysctl.resources | nindent 12 }} + {{- else if ne .Values.sysctl.resourcesPreset "none" }} + resources: {{- include "common.resources.preset" (dict "type" .Values.sysctl.resourcesPreset) | nindent 12 }} + {{- end }} + {{- if .Values.sysctl.mountHostSys }} + volumeMounts: + - name: empty-dir + mountPath: /tmp + subPath: tmp-dir + - name: host-sys + mountPath: /host-sys + {{- end }} + {{- end }} + {{- end }} + volumes: + - name: start-scripts + configMap: + name: {{ printf "%s-scripts" (include "common.names.fullname" .) }} + defaultMode: 0755 + - name: health + configMap: + name: {{ printf "%s-health" (include "common.names.fullname" .) }} + defaultMode: 0755 + {{- if or .Values.sentinel.masterService.enabled .Values.sentinel.service.createMaster}} + - name: kubectl-shared + emptyDir: {} + - name: kubectl-scripts + configMap: + name: {{ printf "%s-kubectl-scripts" (include "common.names.fullname" .) }} + defaultMode: 0755 + {{- end }} + {{- if .Values.auth.usePasswordFiles }} + - name: redis-password + {{ if .Values.auth.usePasswordFileFromSecret }} + secret: + secretName: {{ template "redis.secretName" . }} + items: + - key: {{ template "redis.secretPasswordKey" . }} + path: redis-password + {{- else }} + emptyDir: {} + {{- end }} + {{- end }} + - name: config + configMap: + name: {{ include "redis.configmapName" . }} + {{- if .Values.sysctl.mountHostSys }} + - name: host-sys + hostPath: + path: /sys + {{- end }} + {{- if not .Values.sentinel.persistence.enabled }} + - name: sentinel-data + {{- if or .Values.sentinel.persistence.medium .Values.sentinel.persistence.sizeLimit }} + emptyDir: + {{- if .Values.sentinel.persistence.medium }} + medium: {{ .Values.sentinel.persistence.medium | quote }} + {{- end }} + {{- if .Values.sentinel.persistence.sizeLimit }} + sizeLimit: {{ .Values.sentinel.persistence.sizeLimit | quote }} + {{- end }} + {{- else }} + emptyDir: {} + {{- end }} + {{- end }} + - name: empty-dir + {{- if or .Values.sentinel.persistence.medium .Values.sentinel.persistence.sizeLimit }} + emptyDir: + {{- if .Values.sentinel.persistence.medium }} + medium: {{ .Values.sentinel.persistence.medium | quote }} + {{- end }} + {{- if .Values.sentinel.persistence.sizeLimit }} + sizeLimit: {{ .Values.sentinel.persistence.sizeLimit | quote }} + {{- end }} + {{- else }} + emptyDir: {} + {{- end }} + {{- if .Values.replica.extraVolumes }} + {{- include "common.tplvalues.render" ( dict "value" .Values.replica.extraVolumes "context" $ ) | nindent 8 }} + {{- end }} + {{- if .Values.metrics.extraVolumes }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.extraVolumes "context" $ ) | nindent 8 }} + {{- end }} + {{- if .Values.sentinel.extraVolumes }} + {{- include "common.tplvalues.render" ( dict "value" .Values.sentinel.extraVolumes "context" $ ) | nindent 8 }} + {{- end }} + {{- if .Values.tls.enabled }} + - name: redis-certificates + secret: + secretName: {{ include "redis.tlsSecretName" . }} + defaultMode: 256 + {{- end }} + {{- if not .Values.replica.persistence.enabled }} + - name: redis-data + {{- if or .Values.replica.persistence.medium .Values.replica.persistence.sizeLimit }} + emptyDir: + {{- if .Values.replica.persistence.medium }} + medium: {{ .Values.replica.persistence.medium | quote }} + {{- end }} + {{- if .Values.replica.persistence.sizeLimit }} + sizeLimit: {{ .Values.replica.persistence.sizeLimit | quote }} + {{- end }} + {{- else }} + emptyDir: {} + {{- end }} + {{- else if .Values.replica.persistence.existingClaim }} + - name: redis-data + persistentVolumeClaim: + claimName: {{ printf "%s" (tpl .Values.replica.persistence.existingClaim .) }} + {{- else }} + {{- if .Values.sentinel.persistentVolumeClaimRetentionPolicy.enabled }} + persistentVolumeClaimRetentionPolicy: + whenDeleted: {{ .Values.sentinel.persistentVolumeClaimRetentionPolicy.whenDeleted }} + whenScaled: {{ .Values.sentinel.persistentVolumeClaimRetentionPolicy.whenScaled }} + {{- end }} + volumeClaimTemplates: + - apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + name: redis-data + labels: {{- include "common.labels.matchLabels" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 10 }} + app.kubernetes.io/component: node + {{- if .Values.replica.persistence.annotations }} + annotations: {{- toYaml .Values.replica.persistence.annotations | nindent 10 }} + {{- end }} + spec: + accessModes: + {{- range .Values.replica.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.replica.persistence.size | quote }} + {{- if .Values.replica.persistence.selector }} + selector: {{- include "common.tplvalues.render" ( dict "value" .Values.replica.persistence.selector "context" $) | nindent 10 }} + {{- end }} + {{- include "common.storage.class" (dict "persistence" .Values.replica.persistence "global" .Values.global) | nindent 8 }} + {{- if .Values.sentinel.persistence.enabled }} + - apiVersion: v1 + kind: PersistentVolumeClaim + metadata: + name: sentinel-data + {{- $claimLabels := include "common.tplvalues.merge" ( dict "values" ( list .Values.sentinel.persistence.labels .Values.commonLabels ) "context" . ) }} + labels: {{- include "common.labels.matchLabels" ( dict "customLabels" $claimLabels "context" $ ) | nindent 10 }} + app.kubernetes.io/component: node + {{- if .Values.sentinel.persistence.annotations }} + annotations: {{- toYaml .Values.sentinel.persistence.annotations | nindent 10 }} + {{- end }} + spec: + accessModes: + {{- range .Values.sentinel.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.sentinel.persistence.size | quote }} + {{- if .Values.sentinel.persistence.selector }} + selector: {{- include "common.tplvalues.render" ( dict "value" .Values.sentinel.persistence.selector "context" $) | nindent 10 }} + {{- end }} + {{- if .Values.sentinel.persistence.dataSource }} + dataSource: {{- include "common.tplvalues.render" (dict "value" .Values.sentinel.persistence.dataSource "context" $) | nindent 10 }} + {{- end }} + {{- include "common.storage.class" (dict "persistence" .Values.sentinel.persistence "global" .Values.global) | nindent 8 }} + {{- end }} + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/serviceaccount.yaml b/charts/open-webui/charts/redis/templates/serviceaccount.yaml new file mode 100644 index 0000000..0b7d39d --- /dev/null +++ b/charts/open-webui/charts/redis/templates/serviceaccount.yaml @@ -0,0 +1,18 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if and .Values.serviceAccount.create .Values.sentinel.enabled }} +apiVersion: v1 +kind: ServiceAccount +automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} +metadata: + name: {{ template "redis.serviceAccountName" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if or .Values.commonAnnotations .Values.serviceAccount.annotations }} + {{- $annotations := include "common.tplvalues.merge" ( dict "values" ( list .Values.serviceAccount.annotations .Values.commonAnnotations ) "context" . ) }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $annotations "context" $) | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/servicemonitor.yaml b/charts/open-webui/charts/redis/templates/servicemonitor.yaml new file mode 100644 index 0000000..1a9f6ee --- /dev/null +++ b/charts/open-webui/charts/redis/templates/servicemonitor.yaml @@ -0,0 +1,82 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if and .Values.metrics.enabled .Values.metrics.serviceMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ template "common.names.fullname" . }} + namespace: {{ default (include "common.names.namespace" .) .Values.metrics.serviceMonitor.namespace | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if .Values.metrics.serviceMonitor.additionalLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.serviceMonitor.additionalLabels "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + endpoints: + - port: {{ .Values.metrics.serviceMonitor.port }} + {{- if .Values.metrics.serviceMonitor.interval }} + interval: {{ .Values.metrics.serviceMonitor.interval }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.scrapeTimeout }} + scrapeTimeout: {{ .Values.metrics.serviceMonitor.scrapeTimeout }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.honorLabels }} + honorLabels: {{ .Values.metrics.serviceMonitor.honorLabels }} + {{- end }} + {{- with concat .Values.metrics.serviceMonitor.relabelings .Values.metrics.serviceMonitor.relabellings }} + relabelings: {{- toYaml . | nindent 6 }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.metricRelabelings }} + metricRelabelings: {{- toYaml .Values.metrics.serviceMonitor.metricRelabelings | nindent 6 }} + {{- end }} + {{- range .Values.metrics.serviceMonitor.additionalEndpoints }} + - port: {{ .port }} + {{- if .interval }} + interval: {{ .interval }} + {{- end }} + {{- if .scrapeTimeout }} + scrapeTimeout: {{ .scrapeTimeout }} + {{- end }} + {{- if .honorLabels }} + honorLabels: {{ .honorLabels }} + {{- end }} + {{- with concat $.Values.metrics.serviceMonitor.relabelings $.Values.metrics.serviceMonitor.relabellings }} + relabelings: {{- toYaml . | nindent 6 }} + {{- end }} + {{- if .metricRelabelings }} + metricRelabelings: {{- toYaml .metricRelabelings | nindent 6 }} + {{- end }} + {{- if .path }} + path: {{ .path }} + {{- end }} + {{- if .params }} + params: + {{- range $key, $value := .params }} + {{ $key }}: + {{- range $value }} + - {{ . | quote }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.podTargetLabels }} + podTargetLabels: {{- toYaml .Values.metrics.serviceMonitor.podTargetLabels | nindent 4 }} + {{- end }} + {{- with .Values.metrics.serviceMonitor.sampleLimit }} + sampleLimit: {{ . }} + {{- end }} + {{- with .Values.metrics.serviceMonitor.targetLimit }} + targetLimit: {{ . }} + {{- end }} + namespaceSelector: + matchNames: + - {{ include "common.names.namespace" . | quote }} + selector: + matchLabels: {{- include "common.labels.matchLabels" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 6 }} + app.kubernetes.io/component: metrics +{{- end }} diff --git a/charts/open-webui/charts/redis/templates/tls-secret.yaml b/charts/open-webui/charts/redis/templates/tls-secret.yaml new file mode 100644 index 0000000..aa1c1a7 --- /dev/null +++ b/charts/open-webui/charts/redis/templates/tls-secret.yaml @@ -0,0 +1,31 @@ +{{- /* +Copyright Broadcom, Inc. All Rights Reserved. +SPDX-License-Identifier: APACHE-2.0 +*/}} + +{{- if (include "redis.createTlsSecret" .) }} +{{- $secretName := printf "%s-crt" (include "common.names.fullname" .) }} +{{- $ca := genCA "redis-ca" 365 }} +{{- $releaseNamespace := (include "common.names.namespace" .) }} +{{- $clusterDomain := .Values.clusterDomain }} +{{- $fullname := include "common.names.fullname" . }} +{{- $serviceName := include "common.names.fullname" . }} +{{- $headlessServiceName := printf "%s-headless" (include "common.names.fullname" .) }} +{{- $masterServiceName := printf "%s-master" (include "common.names.fullname" .) }} +{{- $altNames := list (printf "*.%s.%s.svc.%s" $serviceName $releaseNamespace $clusterDomain) (printf "%s.%s.svc.%s" $masterServiceName $releaseNamespace $clusterDomain) (printf "*.%s.%s.svc.%s" $masterServiceName $releaseNamespace $clusterDomain) (printf "*.%s.%s.svc.%s" $headlessServiceName $releaseNamespace $clusterDomain) (printf "%s.%s.svc.%s" $headlessServiceName $releaseNamespace $clusterDomain) "127.0.0.1" "localhost" $fullname }} +{{- $cert := genSignedCert $fullname nil $altNames 365 $ca }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secretName }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: kubernetes.io/tls +data: + tls.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.crt" "defaultValue" $cert.Cert "context" $) }} + tls.key: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "tls.key" "defaultValue" $cert.Key "context" $) }} + ca.crt: {{ include "common.secrets.lookup" (dict "secret" $secretName "key" "ca.crt" "defaultValue" $ca.Cert "context" $) }} +{{- end }} diff --git a/charts/open-webui/charts/redis/values.schema.json b/charts/open-webui/charts/redis/values.schema.json new file mode 100644 index 0000000..f6140be --- /dev/null +++ b/charts/open-webui/charts/redis/values.schema.json @@ -0,0 +1,3281 @@ +{ + "$schema": "http://json-schema.org/schema#", + "type": "object", + "properties": { + "global": { + "type": "object", + "properties": { + "imageRegistry": { + "type": "string", + "description": "Global Docker image registry", + "default": "" + }, + "imagePullSecrets": { + "type": "array", + "description": "Global Docker registry secret names as an array", + "default": [], + "items": {} + }, + "defaultStorageClass": { + "type": "string", + "description": "Global default StorageClass for Persistent Volume(s)", + "default": "" + }, + "storageClass": { + "type": "string", + "description": "DEPRECATED: use global.defaultStorageClass instead", + "default": "" + }, + "redis": { + "type": "object", + "properties": { + "password": { + "type": "string", + "description": "Global Redis® password (overrides `auth.password`)", + "default": "" + } + } + }, + "compatibility": { + "type": "object", + "properties": { + "openshift": { + "type": "object", + "properties": { + "adaptSecurityContext": { + "type": "string", + "description": "Adapt the securityContext sections of the deployment to make them compatible with Openshift restricted-v2 SCC: remove runAsUser, runAsGroup and fsGroup and let the platform use their allowed default IDs. Possible values: auto (apply if the detected running cluster is Openshift), force (perform the adaptation always), disabled (do not perform adaptation)", + "default": "auto" + } + } + } + } + } + } + }, + "kubeVersion": { + "type": "string", + "description": "Override Kubernetes version", + "default": "" + }, + "nameOverride": { + "type": "string", + "description": "String to partially override common.names.fullname", + "default": "" + }, + "fullnameOverride": { + "type": "string", + "description": "String to fully override common.names.fullname", + "default": "" + }, + "namespaceOverride": { + "type": "string", + "description": "String to fully override common.names.namespace", + "default": "" + }, + "commonLabels": { + "type": ["object", "string"], + "description": "Labels to add to all deployed objects", + "default": {} + }, + "commonAnnotations": { + "type": ["object", "string"], + "description": "Annotations to add to all deployed objects", + "default": {} + }, + "secretAnnotations": { + "type": ["object", "string"], + "description": "Annotations to add to secret", + "default": {} + }, + "clusterDomain": { + "type": "string", + "description": "Kubernetes cluster domain name", + "default": "cluster.local" + }, + "extraDeploy": { + "type": "array", + "description": "Array of extra objects to deploy with the release", + "default": [], + "items": {} + }, + "useHostnames": { + "type": "boolean", + "description": "Use hostnames internally when announcing replication. If false, the hostname will be resolved to an IP address", + "default": true + }, + "nameResolutionThreshold": { + "type": "number", + "description": "Failure threshold for internal hostnames resolution", + "default": 5 + }, + "nameResolutionTimeout": { + "type": "number", + "description": "Timeout seconds between probes for internal hostnames resolution", + "default": 5 + }, + "diagnosticMode": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable diagnostic mode (all probes will be disabled and the command will be overridden)", + "default": false + }, + "command": { + "type": ["array", "string"], + "description": "Command to override all containers in the deployment", + "default": [ + "sleep" + ], + "items": { + "type": "string" + } + }, + "args": { + "type": ["array", "string"], + "description": "Args to override all containers in the deployment", + "default": [ + "infinity" + ], + "items": { + "type": "string" + } + } + } + }, + "image": { + "type": "object", + "properties": { + "registry": { + "type": "string", + "description": "Redis® image registry", + "default": "REGISTRY_NAME" + }, + "repository": { + "type": "string", + "description": "Redis® image repository", + "default": "REPOSITORY_NAME/redis" + }, + "digest": { + "type": "string", + "description": "Redis® image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag", + "default": "" + }, + "pullPolicy": { + "type": "string", + "description": "Redis® image pull policy", + "default": "IfNotPresent" + }, + "pullSecrets": { + "type": "array", + "description": "Redis® image pull secrets", + "default": [], + "items": {} + }, + "debug": { + "type": "boolean", + "description": "Enable image debug mode", + "default": false + } + } + }, + "architecture": { + "type": "string", + "description": "Redis® architecture. Allowed values: `standalone` or `replication`", + "default": "replication" + }, + "auth": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable password authentication", + "default": true + }, + "sentinel": { + "type": "boolean", + "description": "Enable password authentication on sentinels too", + "default": true + }, + "password": { + "type": "string", + "description": "Redis® password", + "default": "" + }, + "existingSecret": { + "type": "string", + "description": "The name of an existing secret with Redis® credentials", + "default": "" + }, + "existingSecretPasswordKey": { + "type": "string", + "description": "Password key to be retrieved from existing secret", + "default": "" + }, + "usePasswordFiles": { + "type": "boolean", + "description": "Mount credentials as files instead of using an environment variable", + "default": false + }, + "usePasswordFileFromSecret": { + "type": "boolean", + "description": "Mount password file from secret", + "default": true + } + } + }, + "commonConfiguration": { + "type": "string", + "description": "Common configuration to be added into the ConfigMap", + "default": "\"\"" + }, + "existingConfigmap": { + "type": "string", + "description": "The name of an existing ConfigMap with your custom configuration for Redis® nodes", + "default": "" + }, + "master": { + "type": "object", + "properties": { + "count": { + "type": "number", + "description": "Number of Redis® master instances to deploy (experimental, requires additional configuration)", + "default": 1 + }, + "revisionHistoryLimit": { + "type": "number", + "description": "The number of old history to retain to allow rollback", + "default": 10 + }, + "configuration": { + "type": "string", + "description": "Configuration for Redis® master nodes", + "default": "" + }, + "disableCommands": { + "type": "array", + "description": "Array with Redis® commands to disable on master nodes", + "default": [ + "FLUSHDB", + "FLUSHALL" + ], + "items": { + "type": "string" + } + }, + "command": { + "type": ["array", "string"], + "description": "Override default container command (useful when using custom images)", + "default": [], + "items": {} + }, + "args": { + "type": ["array", "string"], + "description": "Override default container args (useful when using custom images)", + "default": [], + "items": {} + }, + "enableServiceLinks": { + "type": "boolean", + "description": "Whether information about services should be injected into pod's environment variable", + "default": true + }, + "preExecCmds": { + "type": "array", + "description": "Additional commands to run prior to starting Redis® master", + "default": [], + "items": {} + }, + "extraFlags": { + "type": "array", + "description": "Array with additional command line flags for Redis® master", + "default": [], + "items": {} + }, + "extraEnvVars": { + "type": ["array", "string"], + "description": "Array with extra environment variables to add to Redis® master nodes", + "default": [], + "items": {} + }, + "extraEnvVarsCM": { + "type": "string", + "description": "Name of existing ConfigMap containing extra env vars for Redis® master nodes", + "default": "" + }, + "extraEnvVarsSecret": { + "type": "string", + "description": "Name of existing Secret containing extra env vars for Redis® master nodes", + "default": "" + }, + "containerPorts": { + "type": "object", + "properties": { + "redis": { + "type": "number", + "description": "Container port to open on Redis® master nodes", + "default": 6379 + } + } + }, + "startupProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable startupProbe on Redis® master nodes", + "default": false + }, + "initialDelaySeconds": { + "type": "number", + "description": "Initial delay seconds for startupProbe", + "default": 20 + }, + "periodSeconds": { + "type": "number", + "description": "Period seconds for startupProbe", + "default": 5 + }, + "timeoutSeconds": { + "type": "number", + "description": "Timeout seconds for startupProbe", + "default": 5 + }, + "failureThreshold": { + "type": "number", + "description": "Failure threshold for startupProbe", + "default": 5 + }, + "successThreshold": { + "type": "number", + "description": "Success threshold for startupProbe", + "default": 1 + } + } + }, + "livenessProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable livenessProbe on Redis® master nodes", + "default": true + }, + "initialDelaySeconds": { + "type": "number", + "description": "Initial delay seconds for livenessProbe", + "default": 20 + }, + "periodSeconds": { + "type": "number", + "description": "Period seconds for livenessProbe", + "default": 5 + }, + "timeoutSeconds": { + "type": "number", + "description": "Timeout seconds for livenessProbe", + "default": 5 + }, + "failureThreshold": { + "type": "number", + "description": "Failure threshold for livenessProbe", + "default": 5 + }, + "successThreshold": { + "type": "number", + "description": "Success threshold for livenessProbe", + "default": 1 + } + } + }, + "readinessProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable readinessProbe on Redis® master nodes", + "default": true + }, + "initialDelaySeconds": { + "type": "number", + "description": "Initial delay seconds for readinessProbe", + "default": 20 + }, + "periodSeconds": { + "type": "number", + "description": "Period seconds for readinessProbe", + "default": 5 + }, + "timeoutSeconds": { + "type": "number", + "description": "Timeout seconds for readinessProbe", + "default": 1 + }, + "failureThreshold": { + "type": "number", + "description": "Failure threshold for readinessProbe", + "default": 5 + }, + "successThreshold": { + "type": "number", + "description": "Success threshold for readinessProbe", + "default": 1 + } + } + }, + "customStartupProbe": { + "type": ["object", "string"], + "description": "Custom startupProbe that overrides the default one", + "default": {} + }, + "customLivenessProbe": { + "type": ["object", "string"], + "description": "Custom livenessProbe that overrides the default one", + "default": {} + }, + "customReadinessProbe": { + "type": ["object", "string"], + "description": "Custom readinessProbe that overrides the default one", + "default": {} + }, + "resourcesPreset": { + "type": "string", + "description": "Set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). This is ignored if master.resources is set (master.resources is recommended for production).", + "default": "nano" + }, + "resources": { + "type": "object", + "description": "Set container requests and limits for different resources like CPU or memory (essential for production workloads)", + "default": {} + }, + "podSecurityContext": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enabled Redis® master pods' Security Context", + "default": true + }, + "fsGroupChangePolicy": { + "type": "string", + "description": "Set filesystem group change policy", + "default": "Always" + }, + "sysctls": { + "type": "array", + "description": "Set kernel settings using the sysctl interface", + "default": [], + "items": {} + }, + "supplementalGroups": { + "type": "array", + "description": "Set filesystem extra groups", + "default": [], + "items": {} + }, + "fsGroup": { + "type": "number", + "description": "Set Redis® master pod's Security Context fsGroup", + "default": 1001 + } + } + }, + "containerSecurityContext": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enabled Redis® master containers' Security Context", + "default": true + }, + "runAsUser": { + "type": "number", + "description": "Set Redis® master containers' Security Context runAsUser", + "default": 1001 + }, + "runAsGroup": { + "type": "number", + "description": "Set Redis® master containers' Security Context runAsGroup", + "default": 1001 + }, + "runAsNonRoot": { + "type": "boolean", + "description": "Set Redis® master containers' Security Context runAsNonRoot", + "default": true + }, + "allowPrivilegeEscalation": { + "type": "boolean", + "description": "Is it possible to escalate Redis® pod(s) privileges", + "default": false + }, + "readOnlyRootFilesystem": { + "type": "boolean", + "description": "Set container's Security Context read-only root filesystem", + "default": true + }, + "seccompProfile": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Set Redis® master containers' Security Context seccompProfile", + "default": "RuntimeDefault" + } + } + }, + "capabilities": { + "type": "object", + "properties": { + "drop": { + "type": "array", + "description": "Set Redis® master containers' Security Context capabilities to drop", + "default": [ + "ALL" + ], + "items": { + "type": "string" + } + } + } + } + } + }, + "kind": { + "type": "string", + "description": "Use either Deployment, StatefulSet (default) or DaemonSet", + "default": "StatefulSet" + }, + "schedulerName": { + "type": "string", + "description": "Alternate scheduler for Redis® master pods", + "default": "" + }, + "updateStrategy": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Redis® master statefulset strategy type", + "default": "RollingUpdate" + } + } + }, + "minReadySeconds": { + "type": "number", + "description": "How many seconds a pod needs to be ready before killing the next, during update", + "default": 0 + }, + "priorityClassName": { + "type": "string", + "description": "Redis® master pods' priorityClassName", + "default": "" + }, + "automountServiceAccountToken": { + "type": "boolean", + "description": "Mount Service Account token in pod", + "default": false + }, + "hostAliases": { + "type": ["array", "string"], + "description": "Redis® master pods host aliases", + "default": [], + "items": {} + }, + "podLabels": { + "type": "object", + "description": "Extra labels for Redis® master pods", + "default": {} + }, + "podAnnotations": { + "type": ["object", "string"], + "description": "Annotations for Redis® master pods", + "default": {} + }, + "shareProcessNamespace": { + "type": "boolean", + "description": "Share a single process namespace between all of the containers in Redis® master pods", + "default": false + }, + "podAffinityPreset": { + "type": "string", + "description": "Pod affinity preset. Ignored if `master.affinity` is set. Allowed values: `soft` or `hard`", + "default": "" + }, + "podAntiAffinityPreset": { + "type": "string", + "description": "Pod anti-affinity preset. Ignored if `master.affinity` is set. Allowed values: `soft` or `hard`", + "default": "soft" + }, + "nodeAffinityPreset": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Node affinity preset type. Ignored if `master.affinity` is set. Allowed values: `soft` or `hard`", + "default": "" + }, + "key": { + "type": "string", + "description": "Node label key to match. Ignored if `master.affinity` is set", + "default": "" + }, + "values": { + "type": "array", + "description": "Node label values to match. Ignored if `master.affinity` is set", + "default": [], + "items": {} + } + } + }, + "affinity": { + "type": ["object", "string"], + "description": "Affinity for Redis® master pods assignment", + "default": {} + }, + "nodeSelector": { + "type": ["object", "string"], + "description": "Node labels for Redis® master pods assignment", + "default": {} + }, + "tolerations": { + "type": ["array", "string"], + "description": "Tolerations for Redis® master pods assignment", + "default": [], + "items": {} + }, + "topologySpreadConstraints": { + "type": ["array", "string"], + "description": "Spread Constraints for Redis® master pod assignment", + "default": [], + "items": {} + }, + "dnsPolicy": { + "type": "string", + "description": "DNS Policy for Redis® master pod", + "default": "" + }, + "dnsConfig": { + "type": ["object", "string"], + "description": "DNS Configuration for Redis® master pod", + "default": {} + }, + "lifecycleHooks": { + "type": ["object", "string"], + "description": "for the Redis® master container(s) to automate configuration before or after startup", + "default": {} + }, + "extraVolumes": { + "type": ["array", "string"], + "description": "Optionally specify extra list of additional volumes for the Redis® master pod(s)", + "default": [], + "items": {} + }, + "extraVolumeMounts": { + "type": ["array", "string"], + "description": "Optionally specify extra list of additional volumeMounts for the Redis® master container(s)", + "default": [], + "items": {} + }, + "sidecars": { + "type": ["array", "string"], + "description": "Add additional sidecar containers to the Redis® master pod(s)", + "default": [], + "items": {} + }, + "initContainers": { + "type": ["array", "string"], + "description": "Add additional init containers to the Redis® master pod(s)", + "default": [], + "items": {} + }, + "persistence": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable persistence on Redis® master nodes using Persistent Volume Claims", + "default": true + }, + "medium": { + "type": "string", + "description": "Provide a medium for `emptyDir` volumes.", + "default": "" + }, + "sizeLimit": { + "type": "string", + "description": "Set this to enable a size limit for `emptyDir` volumes.", + "default": "" + }, + "path": { + "type": "string", + "description": "The path the volume will be mounted at on Redis® master containers", + "default": "/data" + }, + "subPath": { + "type": "string", + "description": "The subdirectory of the volume to mount on Redis® master containers", + "default": "" + }, + "subPathExpr": { + "type": "string", + "description": "Used to construct the subPath subdirectory of the volume to mount on Redis® master containers", + "default": "" + }, + "storageClass": { + "type": "string", + "description": "Persistent Volume storage class", + "default": "" + }, + "accessModes": { + "type": "array", + "description": "Persistent Volume access modes", + "default": [ + "ReadWriteOnce" + ], + "items": { + "type": "string" + } + }, + "size": { + "type": "string", + "description": "Persistent Volume size", + "default": "8Gi" + }, + "annotations": { + "type": "object", + "description": "Additional custom annotations for the PVC", + "default": {} + }, + "labels": { + "type": ["object", "string"], + "description": "Additional custom labels for the PVC", + "default": {} + }, + "selector": { + "type": ["object", "string"], + "description": "Additional labels to match for the PVC", + "default": {} + }, + "dataSource": { + "type": ["object", "string"], + "description": "Custom PVC data source", + "default": {} + }, + "existingClaim": { + "type": "string", + "description": "Use a existing PVC which must be created manually before bound", + "default": "" + } + } + }, + "persistentVolumeClaimRetentionPolicy": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Controls if and how PVCs are deleted during the lifecycle of a StatefulSet", + "default": false + }, + "whenScaled": { + "type": "string", + "description": "Volume retention behavior when the replica count of the StatefulSet is reduced", + "default": "Retain" + }, + "whenDeleted": { + "type": "string", + "description": "Volume retention behavior that applies when the StatefulSet is deleted", + "default": "Retain" + } + } + }, + "service": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Redis® master service type", + "default": "ClusterIP" + }, + "portNames": { + "type": "object", + "properties": { + "redis": { + "type": "string", + "description": "Redis® master service port name", + "default": "tcp-redis" + } + } + }, + "ports": { + "type": "object", + "properties": { + "redis": { + "type": "number", + "description": "Redis® master service port", + "default": 6379 + } + } + }, + "nodePorts": { + "type": "object", + "properties": { + "redis": { + "type": "string", + "description": "Node port for Redis® master", + "default": "" + } + } + }, + "externalTrafficPolicy": { + "type": "string", + "description": "Redis® master service external traffic policy", + "default": "Cluster" + }, + "extraPorts": { + "type": ["array", "string"], + "description": "Extra ports to expose (normally used with the `sidecar` value)", + "default": [], + "items": {} + }, + "internalTrafficPolicy": { + "type": "string", + "description": "Redis® master service internal traffic policy (requires Kubernetes v1.22 or greater to be usable)", + "default": "Cluster" + }, + "clusterIP": { + "type": "string", + "description": "Redis® master service Cluster IP", + "default": "" + }, + "loadBalancerIP": { + "type": "string", + "description": "Redis® master service Load Balancer IP", + "default": "" + }, + "loadBalancerClass": { + "type": "string", + "description": "master service Load Balancer class if service type is `LoadBalancer` (optional, cloud specific)", + "default": "" + }, + "loadBalancerSourceRanges": { + "type": "array", + "description": "Redis® master service Load Balancer sources", + "default": [], + "items": {} + }, + "externalIPs": { + "type": ["array", "string"], + "description": "Redis® master service External IPs", + "default": [], + "items": {} + }, + "annotations": { + "type": ["object", "string"], + "description": "Additional custom annotations for Redis® master service", + "default": {} + }, + "sessionAffinity": { + "type": "string", + "description": "Session Affinity for Kubernetes service, can be \"None\" or \"ClientIP\"", + "default": "None" + }, + "sessionAffinityConfig": { + "type": ["object", "string"], + "description": "Additional settings for the sessionAffinity", + "default": {} + } + } + }, + "terminationGracePeriodSeconds": { + "type": "number", + "description": "Integer setting the termination grace period for the redis-master pods", + "default": 30 + }, + "serviceAccount": { + "type": "object", + "properties": { + "create": { + "type": "boolean", + "description": "Specifies whether a ServiceAccount should be created", + "default": true + }, + "name": { + "type": "string", + "description": "The name of the ServiceAccount to use.", + "default": "" + }, + "automountServiceAccountToken": { + "type": "boolean", + "description": "Whether to auto mount the service account token", + "default": false + }, + "annotations": { + "type": ["object", "string"], + "description": "Additional custom annotations for the ServiceAccount", + "default": {} + } + } + }, + "pdb": { + "type": "object", + "properties": { + "create": { + "type": "boolean", + "description": "Enable/disable a Pod Disruption Budget creation", + "default": true + } + } + } + } + }, + "replica": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "description": "Use either DaemonSet or StatefulSet (default)", + "default": "StatefulSet" + }, + "replicaCount": { + "type": "number", + "description": "Number of Redis® replicas to deploy", + "default": 3 + }, + "revisionHistoryLimit": { + "type": "number", + "description": "The number of old history to retain to allow rollback", + "default": 10 + }, + "configuration": { + "type": "string", + "description": "Configuration for Redis® replicas nodes", + "default": "" + }, + "disableCommands": { + "type": "array", + "description": "Array with Redis® commands to disable on replicas nodes", + "default": [ + "FLUSHDB", + "FLUSHALL" + ], + "items": { + "type": "string" + } + }, + "command": { + "type": ["array", "string"], + "description": "Override default container command (useful when using custom images)", + "default": [], + "items": {} + }, + "args": { + "type": ["array", "string"], + "description": "Override default container args (useful when using custom images)", + "default": [], + "items": {} + }, + "enableServiceLinks": { + "type": "boolean", + "description": "Whether information about services should be injected into pod's environment variable", + "default": true + }, + "preExecCmds": { + "type": "array", + "description": "Additional commands to run prior to starting Redis® replicas", + "default": [], + "items": {} + }, + "extraFlags": { + "type": "array", + "description": "Array with additional command line flags for Redis® replicas", + "default": [], + "items": {} + }, + "extraEnvVars": { + "type": ["array", "string"], + "description": "Array with extra environment variables to add to Redis® replicas nodes", + "default": [], + "items": {} + }, + "extraEnvVarsCM": { + "type": "string", + "description": "Name of existing ConfigMap containing extra env vars for Redis® replicas nodes", + "default": "" + }, + "extraEnvVarsSecret": { + "type": "string", + "description": "Name of existing Secret containing extra env vars for Redis® replicas nodes", + "default": "" + }, + "externalMaster": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Use external master for bootstrapping", + "default": false + }, + "host": { + "type": "string", + "description": "External master host to bootstrap from", + "default": "" + }, + "port": { + "type": "number", + "description": "Port for Redis service external master host", + "default": 6379 + } + } + }, + "containerPorts": { + "type": "object", + "properties": { + "redis": { + "type": "number", + "description": "Container port to open on Redis® replicas nodes", + "default": 6379 + } + } + }, + "startupProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable startupProbe on Redis® replicas nodes", + "default": true + }, + "initialDelaySeconds": { + "type": "number", + "description": "Initial delay seconds for startupProbe", + "default": 10 + }, + "periodSeconds": { + "type": "number", + "description": "Period seconds for startupProbe", + "default": 10 + }, + "timeoutSeconds": { + "type": "number", + "description": "Timeout seconds for startupProbe", + "default": 5 + }, + "failureThreshold": { + "type": "number", + "description": "Failure threshold for startupProbe", + "default": 22 + }, + "successThreshold": { + "type": "number", + "description": "Success threshold for startupProbe", + "default": 1 + } + } + }, + "livenessProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable livenessProbe on Redis® replicas nodes", + "default": true + }, + "initialDelaySeconds": { + "type": "number", + "description": "Initial delay seconds for livenessProbe", + "default": 20 + }, + "periodSeconds": { + "type": "number", + "description": "Period seconds for livenessProbe", + "default": 5 + }, + "timeoutSeconds": { + "type": "number", + "description": "Timeout seconds for livenessProbe", + "default": 5 + }, + "failureThreshold": { + "type": "number", + "description": "Failure threshold for livenessProbe", + "default": 5 + }, + "successThreshold": { + "type": "number", + "description": "Success threshold for livenessProbe", + "default": 1 + } + } + }, + "readinessProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable readinessProbe on Redis® replicas nodes", + "default": true + }, + "initialDelaySeconds": { + "type": "number", + "description": "Initial delay seconds for readinessProbe", + "default": 20 + }, + "periodSeconds": { + "type": "number", + "description": "Period seconds for readinessProbe", + "default": 5 + }, + "timeoutSeconds": { + "type": "number", + "description": "Timeout seconds for readinessProbe", + "default": 1 + }, + "failureThreshold": { + "type": "number", + "description": "Failure threshold for readinessProbe", + "default": 5 + }, + "successThreshold": { + "type": "number", + "description": "Success threshold for readinessProbe", + "default": 1 + } + } + }, + "customStartupProbe": { + "type": ["object", "string"], + "description": "Custom startupProbe that overrides the default one", + "default": {} + }, + "customLivenessProbe": { + "type": ["object", "string"], + "description": "Custom livenessProbe that overrides the default one", + "default": {} + }, + "customReadinessProbe": { + "type": ["object", "string"], + "description": "Custom readinessProbe that overrides the default one", + "default": {} + }, + "resourcesPreset": { + "type": "string", + "description": "Set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). This is ignored if replica.resources is set (replica.resources is recommended for production).", + "default": "nano" + }, + "resources": { + "type": "object", + "description": "Set container requests and limits for different resources like CPU or memory (essential for production workloads)", + "default": {} + }, + "podSecurityContext": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enabled Redis® replicas pods' Security Context", + "default": true + }, + "fsGroupChangePolicy": { + "type": "string", + "description": "Set filesystem group change policy", + "default": "Always" + }, + "sysctls": { + "type": "array", + "description": "Set kernel settings using the sysctl interface", + "default": [], + "items": {} + }, + "supplementalGroups": { + "type": "array", + "description": "Set filesystem extra groups", + "default": [], + "items": {} + }, + "fsGroup": { + "type": "number", + "description": "Set Redis® replicas pod's Security Context fsGroup", + "default": 1001 + } + } + }, + "containerSecurityContext": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enabled Redis® replicas containers' Security Context", + "default": true + }, + "runAsUser": { + "type": "number", + "description": "Set Redis® replicas containers' Security Context runAsUser", + "default": 1001 + }, + "runAsGroup": { + "type": "number", + "description": "Set Redis® replicas containers' Security Context runAsGroup", + "default": 1001 + }, + "runAsNonRoot": { + "type": "boolean", + "description": "Set Redis® replicas containers' Security Context runAsNonRoot", + "default": true + }, + "allowPrivilegeEscalation": { + "type": "boolean", + "description": "Set Redis® replicas pod's Security Context allowPrivilegeEscalation", + "default": false + }, + "readOnlyRootFilesystem": { + "type": "boolean", + "description": "Set container's Security Context read-only root filesystem", + "default": true + }, + "seccompProfile": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Set Redis® replicas containers' Security Context seccompProfile", + "default": "RuntimeDefault" + } + } + }, + "capabilities": { + "type": "object", + "properties": { + "drop": { + "type": "array", + "description": "Set Redis® replicas containers' Security Context capabilities to drop", + "default": [ + "ALL" + ], + "items": { + "type": "string" + } + } + } + } + } + }, + "schedulerName": { + "type": "string", + "description": "Alternate scheduler for Redis® replicas pods", + "default": "" + }, + "updateStrategy": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Redis® replicas statefulset strategy type", + "default": "RollingUpdate" + } + } + }, + "minReadySeconds": { + "type": "number", + "description": "How many seconds a pod needs to be ready before killing the next, during update", + "default": 0 + }, + "priorityClassName": { + "type": "string", + "description": "Redis® replicas pods' priorityClassName", + "default": "" + }, + "podManagementPolicy": { + "type": "string", + "description": "podManagementPolicy to manage scaling operation of %%MAIN_CONTAINER_NAME%% pods", + "default": "" + }, + "automountServiceAccountToken": { + "type": "boolean", + "description": "Mount Service Account token in pod", + "default": false + }, + "hostAliases": { + "type": ["array", "string"], + "description": "Redis® replicas pods host aliases", + "default": [], + "items": {} + }, + "podLabels": { + "type": "object", + "description": "Extra labels for Redis® replicas pods", + "default": {} + }, + "podAnnotations": { + "type": ["object", "string"], + "description": "Annotations for Redis® replicas pods", + "default": {} + }, + "shareProcessNamespace": { + "type": "boolean", + "description": "Share a single process namespace between all of the containers in Redis® replicas pods", + "default": false + }, + "podAffinityPreset": { + "type": "string", + "description": "Pod affinity preset. Ignored if `replica.affinity` is set. Allowed values: `soft` or `hard`", + "default": "" + }, + "podAntiAffinityPreset": { + "type": "string", + "description": "Pod anti-affinity preset. Ignored if `replica.affinity` is set. Allowed values: `soft` or `hard`", + "default": "soft" + }, + "nodeAffinityPreset": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Node affinity preset type. Ignored if `replica.affinity` is set. Allowed values: `soft` or `hard`", + "default": "" + }, + "key": { + "type": "string", + "description": "Node label key to match. Ignored if `replica.affinity` is set", + "default": "" + }, + "values": { + "type": "array", + "description": "Node label values to match. Ignored if `replica.affinity` is set", + "default": [], + "items": {} + } + } + }, + "affinity": { + "type": ["object", "string"], + "description": "Affinity for Redis® replicas pods assignment", + "default": {} + }, + "nodeSelector": { + "type": ["object", "string"], + "description": "Node labels for Redis® replicas pods assignment", + "default": {} + }, + "tolerations": { + "type": ["array", "string"], + "description": "Tolerations for Redis® replicas pods assignment", + "default": [], + "items": {} + }, + "topologySpreadConstraints": { + "type": ["array", "string"], + "description": "Spread Constraints for Redis® replicas pod assignment", + "default": [], + "items": {} + }, + "dnsPolicy": { + "type": "string", + "description": "DNS Policy for Redis® replica pods", + "default": "" + }, + "dnsConfig": { + "type": ["object", "string"], + "description": "DNS Configuration for Redis® replica pods", + "default": {} + }, + "lifecycleHooks": { + "type": ["object", "string"], + "description": "for the Redis® replica container(s) to automate configuration before or after startup", + "default": {} + }, + "extraVolumes": { + "type": ["array", "string"], + "description": "Optionally specify extra list of additional volumes for the Redis® replicas pod(s)", + "default": [], + "items": {} + }, + "extraVolumeMounts": { + "type": ["array", "string"], + "description": "Optionally specify extra list of additional volumeMounts for the Redis® replicas container(s)", + "default": [], + "items": {} + }, + "sidecars": { + "type": ["array", "string"], + "description": "Add additional sidecar containers to the Redis® replicas pod(s)", + "default": [], + "items": {} + }, + "initContainers": { + "type": ["array", "string"], + "description": "Add additional init containers to the Redis® replicas pod(s)", + "default": [], + "items": {} + }, + "persistence": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable persistence on Redis® replicas nodes using Persistent Volume Claims", + "default": true + }, + "medium": { + "type": "string", + "description": "Provide a medium for `emptyDir` volumes.", + "default": "" + }, + "sizeLimit": { + "type": "string", + "description": "Set this to enable a size limit for `emptyDir` volumes.", + "default": "" + }, + "path": { + "type": "string", + "description": "The path the volume will be mounted at on Redis® replicas containers", + "default": "/data" + }, + "subPath": { + "type": "string", + "description": "The subdirectory of the volume to mount on Redis® replicas containers", + "default": "" + }, + "subPathExpr": { + "type": "string", + "description": "Used to construct the subPath subdirectory of the volume to mount on Redis® replicas containers", + "default": "" + }, + "storageClass": { + "type": "string", + "description": "Persistent Volume storage class", + "default": "" + }, + "accessModes": { + "type": "array", + "description": "Persistent Volume access modes", + "default": [ + "ReadWriteOnce" + ], + "items": { + "type": "string" + } + }, + "size": { + "type": "string", + "description": "Persistent Volume size", + "default": "8Gi" + }, + "annotations": { + "type": "object", + "description": "Additional custom annotations for the PVC", + "default": {} + }, + "labels": { + "type": ["object", "string"], + "description": "Additional custom labels for the PVC", + "default": {} + }, + "selector": { + "type": ["object", "string"], + "description": "Additional labels to match for the PVC", + "default": {} + }, + "dataSource": { + "type": ["object", "string"], + "description": "Custom PVC data source", + "default": {} + }, + "existingClaim": { + "type": "string", + "description": "Use a existing PVC which must be created manually before bound", + "default": "" + } + } + }, + "persistentVolumeClaimRetentionPolicy": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Controls if and how PVCs are deleted during the lifecycle of a StatefulSet", + "default": false + }, + "whenScaled": { + "type": "string", + "description": "Volume retention behavior when the replica count of the StatefulSet is reduced", + "default": "Retain" + }, + "whenDeleted": { + "type": "string", + "description": "Volume retention behavior that applies when the StatefulSet is deleted", + "default": "Retain" + } + } + }, + "service": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Redis® replicas service type", + "default": "ClusterIP" + }, + "ports": { + "type": "object", + "properties": { + "redis": { + "type": "number", + "description": "Redis® replicas service port", + "default": 6379 + } + } + }, + "nodePorts": { + "type": "object", + "properties": { + "redis": { + "type": "string", + "description": "Node port for Redis® replicas", + "default": "" + } + } + }, + "externalTrafficPolicy": { + "type": "string", + "description": "Redis® replicas service external traffic policy", + "default": "Cluster" + }, + "internalTrafficPolicy": { + "type": "string", + "description": "Redis® replicas service internal traffic policy (requires Kubernetes v1.22 or greater to be usable)", + "default": "Cluster" + }, + "extraPorts": { + "type": ["array", "string"], + "description": "Extra ports to expose (normally used with the `sidecar` value)", + "default": [], + "items": {} + }, + "clusterIP": { + "type": "string", + "description": "Redis® replicas service Cluster IP", + "default": "" + }, + "loadBalancerIP": { + "type": "string", + "description": "Redis® replicas service Load Balancer IP", + "default": "" + }, + "loadBalancerClass": { + "type": "string", + "description": "replicas service Load Balancer class if service type is `LoadBalancer` (optional, cloud specific)", + "default": "" + }, + "loadBalancerSourceRanges": { + "type": "array", + "description": "Redis® replicas service Load Balancer sources", + "default": [], + "items": {} + }, + "annotations": { + "type": ["object", "string"], + "description": "Additional custom annotations for Redis® replicas service", + "default": {} + }, + "sessionAffinity": { + "type": "string", + "description": "Session Affinity for Kubernetes service, can be \"None\" or \"ClientIP\"", + "default": "None" + }, + "sessionAffinityConfig": { + "type": ["object", "string"], + "description": "Additional settings for the sessionAffinity", + "default": {} + } + } + }, + "terminationGracePeriodSeconds": { + "type": "number", + "description": "Integer setting the termination grace period for the redis-replicas pods", + "default": 30 + }, + "autoscaling": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable replica autoscaling settings", + "default": false + }, + "minReplicas": { + "type": "number", + "description": "Minimum replicas for the pod autoscaling", + "default": 1 + }, + "maxReplicas": { + "type": "number", + "description": "Maximum replicas for the pod autoscaling", + "default": 11 + }, + "targetCPU": { + "type": "string", + "description": "Percentage of CPU to consider when autoscaling", + "default": "" + }, + "targetMemory": { + "type": "string", + "description": "Percentage of Memory to consider when autoscaling", + "default": "" + } + } + }, + "serviceAccount": { + "type": "object", + "properties": { + "create": { + "type": "boolean", + "description": "Specifies whether a ServiceAccount should be created", + "default": true + }, + "name": { + "type": "string", + "description": "The name of the ServiceAccount to use.", + "default": "" + }, + "automountServiceAccountToken": { + "type": "boolean", + "description": "Whether to auto mount the service account token", + "default": false + }, + "annotations": { + "type": ["object", "string"], + "description": "Additional custom annotations for the ServiceAccount", + "default": {} + } + } + }, + "pdb": { + "type": "object", + "properties": { + "create": { + "type": "boolean", + "description": "Enable/disable a Pod Disruption Budget creation", + "default": true + } + } + } + } + }, + "sentinel": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Use Redis® Sentinel on Redis® pods.", + "default": false + }, + "image": { + "type": "object", + "properties": { + "registry": { + "type": "string", + "description": "Redis® Sentinel image registry", + "default": "REGISTRY_NAME" + }, + "repository": { + "type": "string", + "description": "Redis® Sentinel image repository", + "default": "REPOSITORY_NAME/redis-sentinel" + }, + "digest": { + "type": "string", + "description": "Redis® Sentinel image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag", + "default": "" + }, + "pullPolicy": { + "type": "string", + "description": "Redis® Sentinel image pull policy", + "default": "IfNotPresent" + }, + "pullSecrets": { + "type": "array", + "description": "Redis® Sentinel image pull secrets", + "default": [], + "items": {} + }, + "debug": { + "type": "boolean", + "description": "Enable image debug mode", + "default": false + } + } + }, + "annotations": { + "type": ["object", "string"], + "description": "Additional custom annotations for Redis® Sentinel resource", + "default": {} + }, + "masterSet": { + "type": "string", + "description": "Master set name", + "default": "mymaster" + }, + "quorum": { + "type": "number", + "description": "Sentinel Quorum", + "default": 2 + }, + "getMasterTimeout": { + "type": "number", + "description": "Amount of time to allow before get_sentinel_master_info() times out.", + "default": 90 + }, + "automateClusterRecovery": { + "type": "boolean", + "description": "Automate cluster recovery in cases where the last replica is not considered a good replica and Sentinel won't automatically failover to it.", + "default": false + }, + "redisShutdownWaitFailover": { + "type": "boolean", + "description": "Whether the Redis® master container waits for the failover at shutdown (in addition to the Redis® Sentinel container).", + "default": true + }, + "downAfterMilliseconds": { + "type": "number", + "description": "Timeout for detecting a Redis® node is down", + "default": 60000 + }, + "failoverTimeout": { + "type": "number", + "description": "Timeout for performing a election failover", + "default": 180000 + }, + "parallelSyncs": { + "type": "number", + "description": "Number of replicas that can be reconfigured in parallel to use the new master after a failover", + "default": 1 + }, + "configuration": { + "type": "string", + "description": "Configuration for Redis® Sentinel nodes", + "default": "" + }, + "command": { + "type": ["array", "string"], + "description": "Override default container command (useful when using custom images)", + "default": [], + "items": {} + }, + "args": { + "type": ["array", "string"], + "description": "Override default container args (useful when using custom images)", + "default": [], + "items": {} + }, + "enableServiceLinks": { + "type": "boolean", + "description": "Whether information about services should be injected into pod's environment variable", + "default": true + }, + "preExecCmds": { + "type": "array", + "description": "Additional commands to run prior to starting Redis® Sentinel", + "default": [], + "items": {} + }, + "extraEnvVars": { + "type": ["array", "string"], + "description": "Array with extra environment variables to add to Redis® Sentinel nodes", + "default": [], + "items": {} + }, + "extraEnvVarsCM": { + "type": "string", + "description": "Name of existing ConfigMap containing extra env vars for Redis® Sentinel nodes", + "default": "" + }, + "extraEnvVarsSecret": { + "type": "string", + "description": "Name of existing Secret containing extra env vars for Redis® Sentinel nodes", + "default": "" + }, + "externalMaster": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Use external master for bootstrapping", + "default": false + }, + "host": { + "type": "string", + "description": "External master host to bootstrap from", + "default": "" + }, + "port": { + "type": "number", + "description": "Port for Redis service external master host", + "default": 6379 + } + } + }, + "containerPorts": { + "type": "object", + "properties": { + "sentinel": { + "type": "number", + "description": "Container port to open on Redis® Sentinel nodes", + "default": 26379 + } + } + }, + "startupProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable startupProbe on Redis® Sentinel nodes", + "default": true + }, + "initialDelaySeconds": { + "type": "number", + "description": "Initial delay seconds for startupProbe", + "default": 10 + }, + "periodSeconds": { + "type": "number", + "description": "Period seconds for startupProbe", + "default": 10 + }, + "timeoutSeconds": { + "type": "number", + "description": "Timeout seconds for startupProbe", + "default": 5 + }, + "failureThreshold": { + "type": "number", + "description": "Failure threshold for startupProbe", + "default": 22 + }, + "successThreshold": { + "type": "number", + "description": "Success threshold for startupProbe", + "default": 1 + } + } + }, + "livenessProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable livenessProbe on Redis® Sentinel nodes", + "default": true + }, + "initialDelaySeconds": { + "type": "number", + "description": "Initial delay seconds for livenessProbe", + "default": 20 + }, + "periodSeconds": { + "type": "number", + "description": "Period seconds for livenessProbe", + "default": 10 + }, + "timeoutSeconds": { + "type": "number", + "description": "Timeout seconds for livenessProbe", + "default": 5 + }, + "failureThreshold": { + "type": "number", + "description": "Failure threshold for livenessProbe", + "default": 6 + }, + "successThreshold": { + "type": "number", + "description": "Success threshold for livenessProbe", + "default": 1 + } + } + }, + "readinessProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable readinessProbe on Redis® Sentinel nodes", + "default": true + }, + "initialDelaySeconds": { + "type": "number", + "description": "Initial delay seconds for readinessProbe", + "default": 20 + }, + "periodSeconds": { + "type": "number", + "description": "Period seconds for readinessProbe", + "default": 5 + }, + "timeoutSeconds": { + "type": "number", + "description": "Timeout seconds for readinessProbe", + "default": 1 + }, + "failureThreshold": { + "type": "number", + "description": "Failure threshold for readinessProbe", + "default": 6 + }, + "successThreshold": { + "type": "number", + "description": "Success threshold for readinessProbe", + "default": 1 + } + } + }, + "customStartupProbe": { + "type": ["object", "string"], + "description": "Custom startupProbe that overrides the default one", + "default": {} + }, + "customLivenessProbe": { + "type": ["object", "string"], + "description": "Custom livenessProbe that overrides the default one", + "default": {} + }, + "customReadinessProbe": { + "type": ["object", "string"], + "description": "Custom readinessProbe that overrides the default one", + "default": {} + }, + "persistence": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable persistence on Redis® sentinel nodes using Persistent Volume Claims (Experimental)", + "default": false + }, + "storageClass": { + "type": "string", + "description": "Persistent Volume storage class", + "default": "" + }, + "accessModes": { + "type": "array", + "description": "Persistent Volume access modes", + "default": [ + "ReadWriteOnce" + ], + "items": { + "type": "string" + } + }, + "size": { + "type": "string", + "description": "Persistent Volume size", + "default": "100Mi" + }, + "annotations": { + "type": "object", + "description": "Additional custom annotations for the PVC", + "default": {} + }, + "labels": { + "type": ["object", "string"], + "description": "Additional custom labels for the PVC", + "default": {} + }, + "selector": { + "type": ["object", "string"], + "description": "Additional labels to match for the PVC", + "default": {} + }, + "dataSource": { + "type": ["object", "string"], + "description": "Custom PVC data source", + "default": {} + }, + "medium": { + "type": "string", + "description": "Provide a medium for `emptyDir` volumes.", + "default": "" + }, + "sizeLimit": { + "type": "string", + "description": "Set this to enable a size limit for `emptyDir` volumes.", + "default": "" + } + } + }, + "persistentVolumeClaimRetentionPolicy": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Controls if and how PVCs are deleted during the lifecycle of a StatefulSet", + "default": false + }, + "whenScaled": { + "type": "string", + "description": "Volume retention behavior when the replica count of the StatefulSet is reduced", + "default": "Retain" + }, + "whenDeleted": { + "type": "string", + "description": "Volume retention behavior that applies when the StatefulSet is deleted", + "default": "Retain" + } + } + }, + "resourcesPreset": { + "type": "string", + "description": "Set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). This is ignored if sentinel.resources is set (sentinel.resources is recommended for production).", + "default": "nano" + }, + "resources": { + "type": "object", + "description": "Set container requests and limits for different resources like CPU or memory (essential for production workloads)", + "default": {} + }, + "containerSecurityContext": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enabled Redis® Sentinel containers' Security Context", + "default": true + }, + "runAsUser": { + "type": "number", + "description": "Set Redis® Sentinel containers' Security Context runAsUser", + "default": 1001 + }, + "runAsGroup": { + "type": "number", + "description": "Set Redis® Sentinel containers' Security Context runAsGroup", + "default": 1001 + }, + "runAsNonRoot": { + "type": "boolean", + "description": "Set Redis® Sentinel containers' Security Context runAsNonRoot", + "default": true + }, + "readOnlyRootFilesystem": { + "type": "boolean", + "description": "Set container's Security Context read-only root filesystem", + "default": true + }, + "allowPrivilegeEscalation": { + "type": "boolean", + "description": "Set Redis® Sentinel containers' Security Context allowPrivilegeEscalation", + "default": false + }, + "seccompProfile": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Set Redis® Sentinel containers' Security Context seccompProfile", + "default": "RuntimeDefault" + } + } + }, + "capabilities": { + "type": "object", + "properties": { + "drop": { + "type": "array", + "description": "Set Redis® Sentinel containers' Security Context capabilities to drop", + "default": [ + "ALL" + ], + "items": { + "type": "string" + } + } + } + } + } + }, + "lifecycleHooks": { + "type": ["object", "string"], + "description": "for the Redis® sentinel container(s) to automate configuration before or after startup", + "default": {} + }, + "extraVolumes": { + "type": ["array", "string"], + "description": "Optionally specify extra list of additional volumes for the Redis® Sentinel", + "default": [], + "items": {} + }, + "extraVolumeMounts": { + "type": ["array", "string"], + "description": "Optionally specify extra list of additional volumeMounts for the Redis® Sentinel container(s)", + "default": [], + "items": {} + }, + "service": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Redis® Sentinel service type", + "default": "ClusterIP" + }, + "ports": { + "type": "object", + "properties": { + "redis": { + "type": "number", + "description": "Redis® service port for Redis®", + "default": 6379 + }, + "sentinel": { + "type": "number", + "description": "Redis® service port for Redis® Sentinel", + "default": 26379 + } + } + }, + "nodePorts": { + "type": "object", + "properties": { + "redis": { + "type": "string", + "description": "Node port for Redis®", + "default": "" + }, + "sentinel": { + "type": "string", + "description": "Node port for Sentinel", + "default": "" + } + } + }, + "externalTrafficPolicy": { + "type": "string", + "description": "Redis® Sentinel service external traffic policy", + "default": "Cluster" + }, + "extraPorts": { + "type": ["array", "string"], + "description": "Extra ports to expose (normally used with the `sidecar` value)", + "default": [], + "items": {} + }, + "clusterIP": { + "type": "string", + "description": "Redis® Sentinel service Cluster IP", + "default": "" + }, + "createMaster": { + "type": "boolean", + "description": "Enable master service pointing to the current master (experimental)", + "default": false + }, + "loadBalancerIP": { + "type": "string", + "description": "Redis® Sentinel service Load Balancer IP", + "default": "" + }, + "loadBalancerClass": { + "type": "string", + "description": "sentinel service Load Balancer class if service type is `LoadBalancer` (optional, cloud specific)", + "default": "" + }, + "loadBalancerSourceRanges": { + "type": "array", + "description": "Redis® Sentinel service Load Balancer sources", + "default": [], + "items": {} + }, + "annotations": { + "type": ["object", "string"], + "description": "Additional custom annotations for Redis® Sentinel service", + "default": {} + }, + "sessionAffinity": { + "type": "string", + "description": "Session Affinity for Kubernetes service, can be \"None\" or \"ClientIP\"", + "default": "None" + }, + "sessionAffinityConfig": { + "type": ["object", "string"], + "description": "Additional settings for the sessionAffinity", + "default": {} + }, + "headless": { + "type": "object", + "properties": { + "annotations": { + "type": ["object", "string"], + "description": "Annotations for the headless service.", + "default": {} + }, + "extraPorts": { + "type": "array", + "description": "Extra ports to expose for the headless service", + "default": [], + "items": {} + } + } + } + } + }, + "masterService": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable master service pointing to the current master (experimental)", + "default": false + }, + "type": { + "type": "string", + "description": "Redis® Sentinel master service type", + "default": "ClusterIP" + }, + "ports": { + "type": "object", + "properties": { + "redis": { + "type": "number", + "description": "Redis® service port for Redis®", + "default": 6379 + } + } + }, + "nodePorts": { + "type": "object", + "properties": { + "redis": { + "type": "string", + "description": "Node port for Redis®", + "default": "" + } + } + }, + "externalTrafficPolicy": { + "type": "string", + "description": "Redis® master service external traffic policy", + "default": "" + }, + "extraPorts": { + "type": ["array", "string"], + "description": "Extra ports to expose (normally used with the `sidecar` value)", + "default": [], + "items": {} + }, + "clusterIP": { + "type": "string", + "description": "Redis® master service Cluster IP", + "default": "" + }, + "loadBalancerIP": { + "type": "string", + "description": "Redis® master service Load Balancer IP", + "default": "" + }, + "loadBalancerClass": { + "type": "string", + "description": "master service Load Balancer class if service type is `LoadBalancer` (optional, cloud specific)", + "default": "" + }, + "loadBalancerSourceRanges": { + "type": "array", + "description": "Redis® master service Load Balancer sources", + "default": [], + "items": {} + }, + "annotations": { + "type": "object", + "description": "Additional custom annotations for Redis® master service", + "default": {} + }, + "sessionAffinity": { + "type": "string", + "description": "Session Affinity for Kubernetes service, can be \"None\" or \"ClientIP\"", + "default": "None" + }, + "sessionAffinityConfig": { + "type": ["object", "string"], + "description": "Additional settings for the sessionAffinity", + "default": {} + } + } + }, + "terminationGracePeriodSeconds": { + "type": "number", + "description": "Integer setting the termination grace period for the redis-node pods", + "default": 30 + } + } + }, + "serviceBindings": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Create secret for service binding (Experimental)", + "default": false + } + } + }, + "networkPolicy": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable creation of NetworkPolicy resources", + "default": true + }, + "allowExternal": { + "type": "boolean", + "description": "Don't require client label for connections", + "default": true + }, + "allowExternalEgress": { + "type": "boolean", + "description": "Allow the pod to access any range of port and all destinations.", + "default": true + }, + "extraIngress": { + "type": ["array", "string"], + "description": "Add extra ingress rules to the NetworkPolicy", + "default": [], + "items": {} + }, + "extraEgress": { + "type": ["array", "string"], + "description": "Add extra egress rules to the NetworkPolicy", + "default": [], + "items": {} + }, + "ingressNSMatchLabels": { + "type": "object", + "description": "Labels to match to allow traffic from other namespaces", + "default": {} + }, + "ingressNSPodMatchLabels": { + "type": "object", + "description": "Pod labels to match to allow traffic from other namespaces", + "default": {} + }, + "metrics": { + "type": "object", + "properties": { + "allowExternal": { + "type": "boolean", + "description": "Don't require client label for connections for metrics endpoint", + "default": true + }, + "ingressNSMatchLabels": { + "type": "object", + "description": "Labels to match to allow traffic from other namespaces to metrics endpoint", + "default": {} + }, + "ingressNSPodMatchLabels": { + "type": "object", + "description": "Pod labels to match to allow traffic from other namespaces to metrics endpoint", + "default": {} + } + } + } + } + }, + "podSecurityPolicy": { + "type": "object", + "properties": { + "create": { + "type": "boolean", + "description": "Whether to create a PodSecurityPolicy. WARNING: PodSecurityPolicy is deprecated in Kubernetes v1.21 or later, unavailable in v1.25 or later", + "default": false + }, + "enabled": { + "type": "boolean", + "description": "Enable PodSecurityPolicy's RBAC rules", + "default": false + } + } + }, + "rbac": { + "type": "object", + "properties": { + "create": { + "type": "boolean", + "description": "Specifies whether RBAC resources should be created", + "default": false + }, + "rules": { + "type": ["array", "string"], + "description": "Custom RBAC rules to set", + "default": [], + "items": {} + } + } + }, + "serviceAccount": { + "type": "object", + "properties": { + "create": { + "type": "boolean", + "description": "Specifies whether a ServiceAccount should be created", + "default": true + }, + "name": { + "type": "string", + "description": "The name of the ServiceAccount to use.", + "default": "" + }, + "automountServiceAccountToken": { + "type": "boolean", + "description": "Whether to auto mount the service account token", + "default": false + }, + "annotations": { + "type": ["object", "string"], + "description": "Additional custom annotations for the ServiceAccount", + "default": {} + } + } + }, + "pdb": { + "type": "object", + "description": "DEPRECATED Please use `master.pdb` and `replica.pdb` values instead", + "default": {} + }, + "tls": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable TLS traffic", + "default": false + }, + "authClients": { + "type": "boolean", + "description": "Require clients to authenticate", + "default": true + }, + "autoGenerated": { + "type": "boolean", + "description": "Enable autogenerated certificates", + "default": false + }, + "existingSecret": { + "type": "string", + "description": "The name of the existing secret that contains the TLS certificates", + "default": "" + }, + "certificatesSecret": { + "type": "string", + "description": "DEPRECATED. Use existingSecret instead.", + "default": "" + }, + "certFilename": { + "type": "string", + "description": "Certificate filename", + "default": "" + }, + "certKeyFilename": { + "type": "string", + "description": "Certificate Key filename", + "default": "" + }, + "certCAFilename": { + "type": "string", + "description": "CA Certificate filename", + "default": "" + }, + "dhParamsFilename": { + "type": "string", + "description": "File containing DH params (in order to support DH based ciphers)", + "default": "" + } + } + }, + "metrics": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Start a sidecar prometheus exporter to expose Redis® metrics", + "default": false + }, + "image": { + "type": "object", + "properties": { + "registry": { + "type": "string", + "description": "Redis® Exporter image registry", + "default": "REGISTRY_NAME" + }, + "repository": { + "type": "string", + "description": "Redis® Exporter image repository", + "default": "REPOSITORY_NAME/redis-exporter" + }, + "digest": { + "type": "string", + "description": "Redis® Exporter image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag", + "default": "" + }, + "pullPolicy": { + "type": "string", + "description": "Redis® Exporter image pull policy", + "default": "IfNotPresent" + }, + "pullSecrets": { + "type": "array", + "description": "Redis® Exporter image pull secrets", + "default": [], + "items": {} + } + } + }, + "containerPorts": { + "type": "object", + "properties": { + "http": { + "type": "number", + "description": "Metrics HTTP container port", + "default": 9121 + } + } + }, + "startupProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable startupProbe on Redis® replicas nodes", + "default": false + }, + "initialDelaySeconds": { + "type": "number", + "description": "Initial delay seconds for startupProbe", + "default": 10 + }, + "periodSeconds": { + "type": "number", + "description": "Period seconds for startupProbe", + "default": 10 + }, + "timeoutSeconds": { + "type": "number", + "description": "Timeout seconds for startupProbe", + "default": 5 + }, + "failureThreshold": { + "type": "number", + "description": "Failure threshold for startupProbe", + "default": 5 + }, + "successThreshold": { + "type": "number", + "description": "Success threshold for startupProbe", + "default": 1 + } + } + }, + "livenessProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable livenessProbe on Redis® replicas nodes", + "default": true + }, + "initialDelaySeconds": { + "type": "number", + "description": "Initial delay seconds for livenessProbe", + "default": 10 + }, + "periodSeconds": { + "type": "number", + "description": "Period seconds for livenessProbe", + "default": 10 + }, + "timeoutSeconds": { + "type": "number", + "description": "Timeout seconds for livenessProbe", + "default": 5 + }, + "failureThreshold": { + "type": "number", + "description": "Failure threshold for livenessProbe", + "default": 5 + }, + "successThreshold": { + "type": "number", + "description": "Success threshold for livenessProbe", + "default": 1 + } + } + }, + "readinessProbe": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable readinessProbe on Redis® replicas nodes", + "default": true + }, + "initialDelaySeconds": { + "type": "number", + "description": "Initial delay seconds for readinessProbe", + "default": 5 + }, + "periodSeconds": { + "type": "number", + "description": "Period seconds for readinessProbe", + "default": 10 + }, + "timeoutSeconds": { + "type": "number", + "description": "Timeout seconds for readinessProbe", + "default": 1 + }, + "failureThreshold": { + "type": "number", + "description": "Failure threshold for readinessProbe", + "default": 3 + }, + "successThreshold": { + "type": "number", + "description": "Success threshold for readinessProbe", + "default": 1 + } + } + }, + "customStartupProbe": { + "type": ["object", "string"], + "description": "Custom startupProbe that overrides the default one", + "default": {} + }, + "customLivenessProbe": { + "type": ["object", "string"], + "description": "Custom livenessProbe that overrides the default one", + "default": {} + }, + "customReadinessProbe": { + "type": ["object", "string"], + "description": "Custom readinessProbe that overrides the default one", + "default": {} + }, + "command": { + "type": ["array", "string"], + "description": "Override default metrics container init command (useful when using custom images)", + "default": [], + "items": {} + }, + "redisTargetHost": { + "type": "string", + "description": "A way to specify an alternative Redis® hostname", + "default": "localhost" + }, + "extraArgs": { + "type": "object", + "description": "Extra arguments for Redis® exporter, for example:", + "default": {} + }, + "extraEnvVars": { + "type": ["array", "string"], + "description": "Array with extra environment variables to add to Redis® exporter", + "default": [], + "items": {} + }, + "containerSecurityContext": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enabled Redis® exporter containers' Security Context", + "default": true + }, + "runAsUser": { + "type": "number", + "description": "Set Redis® exporter containers' Security Context runAsUser", + "default": 1001 + }, + "runAsGroup": { + "type": "number", + "description": "Set Redis® exporter containers' Security Context runAsGroup", + "default": 1001 + }, + "runAsNonRoot": { + "type": "boolean", + "description": "Set Redis® exporter containers' Security Context runAsNonRoot", + "default": true + }, + "allowPrivilegeEscalation": { + "type": "boolean", + "description": "Set Redis® exporter containers' Security Context allowPrivilegeEscalation", + "default": false + }, + "readOnlyRootFilesystem": { + "type": "boolean", + "description": "Set container's Security Context read-only root filesystem", + "default": true + }, + "seccompProfile": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Set Redis® exporter containers' Security Context seccompProfile", + "default": "RuntimeDefault" + } + } + }, + "capabilities": { + "type": "object", + "properties": { + "drop": { + "type": "array", + "description": "Set Redis® exporter containers' Security Context capabilities to drop", + "default": [ + "ALL" + ], + "items": { + "type": "string" + } + } + } + } + } + }, + "extraVolumes": { + "type": ["array", "string"], + "description": "Optionally specify extra list of additional volumes for the Redis® metrics sidecar", + "default": [], + "items": {} + }, + "extraVolumeMounts": { + "type": ["array", "string"], + "description": "Optionally specify extra list of additional volumeMounts for the Redis® metrics sidecar", + "default": [], + "items": {} + }, + "resourcesPreset": { + "type": "string", + "description": "Set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). This is ignored if metrics.resources is set (metrics.resources is recommended for production).", + "default": "nano" + }, + "resources": { + "type": "object", + "description": "Set container requests and limits for different resources like CPU or memory (essential for production workloads)", + "default": {} + }, + "podLabels": { + "type": "object", + "description": "Extra labels for Redis® exporter pods", + "default": {} + }, + "service": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Create Service resource(s) for scraping metrics using PrometheusOperator ServiceMonitor, can be disabled when using a PodMonitor", + "default": true + }, + "type": { + "type": "string", + "description": "Redis® exporter service type", + "default": "ClusterIP" + }, + "ports": { + "type": "object", + "properties": { + "http": { + "type": "number", + "description": "Redis® exporter service port", + "default": 9121 + } + } + }, + "externalTrafficPolicy": { + "type": "string", + "description": "Redis® exporter service external traffic policy", + "default": "Cluster" + }, + "extraPorts": { + "type": ["array", "string"], + "description": "Extra ports to expose (normally used with the `sidecar` value)", + "default": [], + "items": {} + }, + "loadBalancerIP": { + "type": "string", + "description": "Redis® exporter service Load Balancer IP", + "default": "" + }, + "loadBalancerClass": { + "type": "string", + "description": "exporter service Load Balancer class if service type is `LoadBalancer` (optional, cloud specific)", + "default": "" + }, + "loadBalancerSourceRanges": { + "type": "array", + "description": "Redis® exporter service Load Balancer sources", + "default": [], + "items": {} + }, + "annotations": { + "type": ["object", "string"], + "description": "Additional custom annotations for Redis® exporter service", + "default": {} + }, + "clusterIP": { + "type": "string", + "description": "Redis® exporter service Cluster IP", + "default": "" + } + } + }, + "serviceMonitor": { + "type": "object", + "properties": { + "port": { + "type": "string", + "description": "the service port to scrape metrics from", + "default": "http-metrics" + }, + "enabled": { + "type": "boolean", + "description": "Create ServiceMonitor resource(s) for scraping metrics using PrometheusOperator", + "default": false + }, + "namespace": { + "type": "string", + "description": "The namespace in which the ServiceMonitor will be created", + "default": "" + }, + "interval": { + "type": "string", + "description": "The interval at which metrics should be scraped", + "default": "30s" + }, + "scrapeTimeout": { + "type": "string", + "description": "The timeout after which the scrape is ended", + "default": "" + }, + "relabelings": { + "type": "array", + "description": "Metrics RelabelConfigs to apply to samples before scraping.", + "default": [], + "items": {} + }, + "metricRelabelings": { + "type": "array", + "description": "Metrics RelabelConfigs to apply to samples before ingestion.", + "default": [], + "items": {} + }, + "honorLabels": { + "type": "boolean", + "description": "Specify honorLabels parameter to add the scrape endpoint", + "default": false + }, + "additionalLabels": { + "type": ["object", "string"], + "description": "Additional labels that can be used so ServiceMonitor resource(s) can be discovered by Prometheus", + "default": {} + }, + "podTargetLabels": { + "type": "array", + "description": "Labels from the Kubernetes pod to be transferred to the created metrics", + "default": [], + "items": {} + }, + "sampleLimit": { + "type": "boolean", + "description": "Limit of how many samples should be scraped from every Pod", + "default": false + }, + "targetLimit": { + "type": "boolean", + "description": "Limit of how many targets should be scraped", + "default": false + }, + "additionalEndpoints": { + "type": "array", + "description": "Additional endpoints to scrape (e.g sentinel)", + "default": [], + "items": {} + } + } + }, + "podMonitor": { + "type": "object", + "properties": { + "port": { + "type": "string", + "description": "the pod port to scrape metrics from", + "default": "metrics" + }, + "enabled": { + "type": "boolean", + "description": "Create PodMonitor resource(s) for scraping metrics using PrometheusOperator", + "default": false + }, + "namespace": { + "type": "string", + "description": "The namespace in which the PodMonitor will be created", + "default": "" + }, + "interval": { + "type": "string", + "description": "The interval at which metrics should be scraped", + "default": "30s" + }, + "scrapeTimeout": { + "type": "string", + "description": "The timeout after which the scrape is ended", + "default": "" + }, + "relabelings": { + "type": "array", + "description": "Metrics RelabelConfigs to apply to samples before scraping.", + "default": [], + "items": {} + }, + "metricRelabelings": { + "type": "array", + "description": "Metrics RelabelConfigs to apply to samples before ingestion.", + "default": [], + "items": {} + }, + "honorLabels": { + "type": "boolean", + "description": "Specify honorLabels parameter to add the scrape endpoint", + "default": false + }, + "additionalLabels": { + "type": ["object", "string"], + "description": "Additional labels that can be used so PodMonitor resource(s) can be discovered by Prometheus", + "default": {} + }, + "podTargetLabels": { + "type": "array", + "description": "Labels from the Kubernetes pod to be transferred to the created metrics", + "default": [], + "items": {} + }, + "sampleLimit": { + "type": "boolean", + "description": "Limit of how many samples should be scraped from every Pod", + "default": false + }, + "targetLimit": { + "type": "boolean", + "description": "Limit of how many targets should be scraped", + "default": false + }, + "additionalEndpoints": { + "type": "array", + "description": "Additional endpoints to scrape (e.g sentinel)", + "default": [], + "items": {} + } + } + }, + "prometheusRule": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Create a custom prometheusRule Resource for scraping metrics using PrometheusOperator", + "default": false + }, + "namespace": { + "type": "string", + "description": "The namespace in which the prometheusRule will be created", + "default": "" + }, + "additionalLabels": { + "type": ["object", "string"], + "description": "Additional labels for the prometheusRule", + "default": {} + }, + "rules": { + "type": ["array", "string"], + "description": "Custom Prometheus rules", + "default": [], + "items": {} + } + } + } + } + }, + "volumePermissions": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable init container that changes the owner/group of the PV mount point to `runAsUser:fsGroup`", + "default": false + }, + "image": { + "type": "object", + "properties": { + "registry": { + "type": "string", + "description": "OS Shell + Utility image registry", + "default": "REGISTRY_NAME" + }, + "repository": { + "type": "string", + "description": "OS Shell + Utility image repository", + "default": "REPOSITORY_NAME/os-shell" + }, + "digest": { + "type": "string", + "description": "OS Shell + Utility image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag", + "default": "" + }, + "pullPolicy": { + "type": "string", + "description": "OS Shell + Utility image pull policy", + "default": "IfNotPresent" + }, + "pullSecrets": { + "type": "array", + "description": "OS Shell + Utility image pull secrets", + "default": [], + "items": {} + } + } + }, + "resourcesPreset": { + "type": "string", + "description": "Set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). This is ignored if volumePermissions.resources is set (volumePermissions.resources is recommended for production).", + "default": "nano" + }, + "resources": { + "type": "object", + "description": "Set container requests and limits for different resources like CPU or memory (essential for production workloads)", + "default": {} + }, + "containerSecurityContext": { + "type": "object", + "properties": { + "runAsUser": { + "type": "number", + "description": "Set init container's Security Context runAsUser", + "default": 0 + } + } + } + } + }, + "kubectl": { + "type": "object", + "properties": { + "image": { + "type": "object", + "properties": { + "registry": { + "type": "string", + "description": "Kubectl image registry", + "default": "REGISTRY_NAME" + }, + "repository": { + "type": "string", + "description": "Kubectl image repository", + "default": "REPOSITORY_NAME/kubectl" + }, + "digest": { + "type": "string", + "description": "Kubectl image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag", + "default": "" + }, + "pullPolicy": { + "type": "string", + "description": "Kubectl image pull policy", + "default": "IfNotPresent" + }, + "pullSecrets": { + "type": "array", + "description": "Kubectl pull secrets", + "default": [], + "items": {} + } + } + }, + "command": { + "type": "array", + "description": "kubectl command to execute", + "default": [ + "/opt/bitnami/scripts/kubectl-scripts/update-master-label.sh" + ], + "items": { + "type": "string" + } + }, + "containerSecurityContext": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enabled kubectl containers' Security Context", + "default": true + }, + "runAsUser": { + "type": "number", + "description": "Set kubectl containers' Security Context runAsUser", + "default": 1001 + }, + "runAsGroup": { + "type": "number", + "description": "Set kubectl containers' Security Context runAsGroup", + "default": 1001 + }, + "runAsNonRoot": { + "type": "boolean", + "description": "Set kubectl containers' Security Context runAsNonRoot", + "default": true + }, + "allowPrivilegeEscalation": { + "type": "boolean", + "description": "Set kubectl containers' Security Context allowPrivilegeEscalation", + "default": false + }, + "readOnlyRootFilesystem": { + "type": "boolean", + "description": "Set container's Security Context read-only root filesystem", + "default": true + }, + "seccompProfile": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Set kubectl containers' Security Context seccompProfile", + "default": "RuntimeDefault" + } + } + }, + "capabilities": { + "type": "object", + "properties": { + "drop": { + "type": "array", + "description": "Set kubectl containers' Security Context capabilities to drop", + "default": [ + "ALL" + ], + "items": { + "type": "string" + } + } + } + } + } + }, + "resources": { + "type": "object", + "properties": { + "limits": { + "type": "object", + "description": "The resources limits for the kubectl containers", + "default": {} + }, + "requests": { + "type": "object", + "description": "The requested resources for the kubectl containers", + "default": {} + } + } + } + } + }, + "sysctl": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable init container to modify Kernel settings", + "default": false + }, + "image": { + "type": "object", + "properties": { + "registry": { + "type": "string", + "description": "OS Shell + Utility image registry", + "default": "REGISTRY_NAME" + }, + "repository": { + "type": "string", + "description": "OS Shell + Utility image repository", + "default": "REPOSITORY_NAME/os-shell" + }, + "digest": { + "type": "string", + "description": "OS Shell + Utility image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag", + "default": "" + }, + "pullPolicy": { + "type": "string", + "description": "OS Shell + Utility image pull policy", + "default": "IfNotPresent" + }, + "pullSecrets": { + "type": "array", + "description": "OS Shell + Utility image pull secrets", + "default": [], + "items": {} + } + } + }, + "command": { + "type": ["array", "string"], + "description": "Override default init-sysctl container command (useful when using custom images)", + "default": [], + "items": {} + }, + "mountHostSys": { + "type": "boolean", + "description": "Mount the host `/sys` folder to `/host-sys`", + "default": false + }, + "resourcesPreset": { + "type": "string", + "description": "Set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). This is ignored if sysctl.resources is set (sysctl.resources is recommended for production).", + "default": "nano" + }, + "resources": { + "type": "object", + "description": "Set container requests and limits for different resources like CPU or memory (essential for production workloads)", + "default": {} + } + } + }, + "useExternalDNS": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable various syntax that would enable external-dns to work. Note this requires a working installation of `external-dns` to be usable.", + "default": false + }, + "additionalAnnotations": { + "type": "object", + "description": "Extra annotations to be utilized when `external-dns` is enabled.", + "default": {} + }, + "annotationKey": { + "type": "string", + "description": "The annotation key utilized when `external-dns` is enabled. Setting this to `false` will disable annotations.", + "default": "external-dns.alpha.kubernetes.io/" + }, + "suffix": { + "type": "string", + "description": "The DNS suffix utilized when `external-dns` is enabled. Note that we prepend the suffix with the full name of the release.", + "default": "" + } + } + } + } +} \ No newline at end of file diff --git a/charts/open-webui/charts/redis/values.yaml b/charts/open-webui/charts/redis/values.yaml new file mode 100644 index 0000000..0c5410b --- /dev/null +++ b/charts/open-webui/charts/redis/values.yaml @@ -0,0 +1,2270 @@ +# Copyright Broadcom, Inc. All Rights Reserved. +# SPDX-License-Identifier: APACHE-2.0 + +## @section Global parameters +## Global Docker image parameters +## Please, note that this will override the image parameters, including dependencies, configured to use the global value +## Current available global Docker image parameters: imageRegistry, imagePullSecrets and storageClass +## + +## @param global.imageRegistry Global Docker image registry +## @param global.imagePullSecrets Global Docker registry secret names as an array +## @param global.defaultStorageClass Global default StorageClass for Persistent Volume(s) +## @param global.storageClass DEPRECATED: use global.defaultStorageClass instead +## @param global.redis.password Global Redis® password (overrides `auth.password`) +## +global: + imageRegistry: "" + ## E.g. + ## imagePullSecrets: + ## - myRegistryKeySecretName + ## + imagePullSecrets: [] + defaultStorageClass: "" + storageClass: "" + ## Security parameters + ## + security: + ## @param global.security.allowInsecureImages Allows skipping image verification + allowInsecureImages: false + redis: + password: "" + ## Compatibility adaptations for Kubernetes platforms + ## + compatibility: + ## Compatibility adaptations for Openshift + ## + openshift: + ## @param global.compatibility.openshift.adaptSecurityContext Adapt the securityContext sections of the deployment to make them compatible with Openshift restricted-v2 SCC: remove runAsUser, runAsGroup and fsGroup and let the platform use their allowed default IDs. Possible values: auto (apply if the detected running cluster is Openshift), force (perform the adaptation always), disabled (do not perform adaptation) + ## + adaptSecurityContext: auto +## @section Common parameters +## + +## @param kubeVersion Override Kubernetes version +## +kubeVersion: "" +## @param nameOverride String to partially override common.names.fullname +## +nameOverride: "" +## @param fullnameOverride String to fully override common.names.fullname +## +fullnameOverride: "" +## @param namespaceOverride String to fully override common.names.namespace +## +namespaceOverride: "" +## @param commonLabels Labels to add to all deployed objects +## +commonLabels: {} +## @param commonAnnotations Annotations to add to all deployed objects +## +commonAnnotations: {} +## @param secretAnnotations Annotations to add to secret +## +secretAnnotations: {} +## @param clusterDomain Kubernetes cluster domain name +## +clusterDomain: cluster.local +## @param extraDeploy Array of extra objects to deploy with the release +## +extraDeploy: [] +## @param useHostnames Use hostnames internally when announcing replication. If false, the hostname will be resolved to an IP address +## +useHostnames: true +## @param nameResolutionThreshold Failure threshold for internal hostnames resolution +## +nameResolutionThreshold: 5 +## @param nameResolutionTimeout Timeout seconds between probes for internal hostnames resolution +## +nameResolutionTimeout: 5 +## Enable diagnostic mode in the deployment +## +diagnosticMode: + ## @param diagnosticMode.enabled Enable diagnostic mode (all probes will be disabled and the command will be overridden) + ## + enabled: false + ## @param diagnosticMode.command Command to override all containers in the deployment + ## + command: + - sleep + ## @param diagnosticMode.args Args to override all containers in the deployment + ## + args: + - infinity +## @section Redis® Image parameters +## + +## Bitnami Redis® image +## ref: https://hub.docker.com/r/bitnami/redis/tags/ +## @param image.registry [default: REGISTRY_NAME] Redis® image registry +## @param image.repository [default: REPOSITORY_NAME/redis] Redis® image repository +## @skip image.tag Redis® image tag (immutable tags are recommended) +## @param image.digest Redis® image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag +## @param image.pullPolicy Redis® image pull policy +## @param image.pullSecrets Redis® image pull secrets +## @param image.debug Enable image debug mode +## +image: + registry: docker.io + repository: bitnami/redis + tag: 7.4.2-debian-12-r0 + digest: "" + ## Specify a imagePullPolicy + ## ref: https://kubernetes.io/docs/concepts/containers/images/#pre-pulled-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## e.g: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + pullSecrets: [] + ## Enable debug mode + ## + debug: false +## @section Redis® common configuration parameters +## https://github.com/bitnami/containers/tree/main/bitnami/redis#configuration +## + +## @param architecture Redis® architecture. Allowed values: `standalone` or `replication` +## +architecture: replication +## Redis® Authentication parameters +## ref: https://github.com/bitnami/containers/tree/main/bitnami/redis#setting-the-server-password-on-first-run +## +auth: + ## @param auth.enabled Enable password authentication + ## + enabled: true + ## @param auth.sentinel Enable password authentication on sentinels too + ## + sentinel: true + ## @param auth.password Redis® password + ## Defaults to a random 10-character alphanumeric string if not set + ## + password: "" + ## @param auth.existingSecret The name of an existing secret with Redis® credentials + ## NOTE: When it's set, the previous `auth.password` parameter is ignored + ## + existingSecret: "" + ## @param auth.existingSecretPasswordKey Password key to be retrieved from existing secret + ## NOTE: ignored unless `auth.existingSecret` parameter is set + ## + existingSecretPasswordKey: "" + ## @param auth.usePasswordFiles Mount credentials as files instead of using an environment variable + ## + usePasswordFiles: false + ## @param auth.usePasswordFileFromSecret Mount password file from secret + ## + usePasswordFileFromSecret: true +## @param commonConfiguration [string] Common configuration to be added into the ConfigMap +## ref: https://redis.io/topics/config +## +commonConfiguration: |- + # Enable AOF https://redis.io/topics/persistence#append-only-file + appendonly yes + # Disable RDB persistence, AOF persistence already enabled. + save "" +## @param existingConfigmap The name of an existing ConfigMap with your custom configuration for Redis® nodes +## +existingConfigmap: "" +## @section Redis® master configuration parameters +## +master: + ## @param master.count Number of Redis® master instances to deploy (experimental, requires additional configuration) + ## + count: 1 + ## @param master.revisionHistoryLimit The number of old history to retain to allow rollback + ## NOTE: Explicitly setting this field to 0, will result in cleaning up all the history, breaking ability to rollback + revisionHistoryLimit: 10 + ## @param master.configuration Configuration for Redis® master nodes + ## ref: https://redis.io/topics/config + ## + configuration: "" + ## @param master.disableCommands Array with Redis® commands to disable on master nodes + ## Commands will be completely disabled by renaming each to an empty string. + ## ref: https://redis.io/topics/security#disabling-of-specific-commands + ## + disableCommands: + - FLUSHDB + - FLUSHALL + ## @param master.command Override default container command (useful when using custom images) + ## + command: [] + ## @param master.args Override default container args (useful when using custom images) + ## + args: [] + ## @param master.enableServiceLinks Whether information about services should be injected into pod's environment variable + ## + enableServiceLinks: true + ## @param master.preExecCmds Additional commands to run prior to starting Redis® master + ## + preExecCmds: [] + ## @param master.extraFlags Array with additional command line flags for Redis® master + ## e.g: + ## extraFlags: + ## - "--maxmemory-policy volatile-ttl" + ## - "--repl-backlog-size 1024mb" + ## + extraFlags: [] + ## @param master.extraEnvVars Array with extra environment variables to add to Redis® master nodes + ## e.g: + ## extraEnvVars: + ## - name: FOO + ## value: "bar" + ## + extraEnvVars: [] + ## @param master.extraEnvVarsCM Name of existing ConfigMap containing extra env vars for Redis® master nodes + ## + extraEnvVarsCM: "" + ## @param master.extraEnvVarsSecret Name of existing Secret containing extra env vars for Redis® master nodes + ## + extraEnvVarsSecret: "" + ## @param master.containerPorts.redis Container port to open on Redis® master nodes + ## + containerPorts: + redis: 6379 + ## Configure extra options for Redis® containers' liveness and readiness probes + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes + ## @param master.startupProbe.enabled Enable startupProbe on Redis® master nodes + ## @param master.startupProbe.initialDelaySeconds Initial delay seconds for startupProbe + ## @param master.startupProbe.periodSeconds Period seconds for startupProbe + ## @param master.startupProbe.timeoutSeconds Timeout seconds for startupProbe + ## @param master.startupProbe.failureThreshold Failure threshold for startupProbe + ## @param master.startupProbe.successThreshold Success threshold for startupProbe + ## + startupProbe: + enabled: false + initialDelaySeconds: 20 + periodSeconds: 5 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + ## @param master.livenessProbe.enabled Enable livenessProbe on Redis® master nodes + ## @param master.livenessProbe.initialDelaySeconds Initial delay seconds for livenessProbe + ## @param master.livenessProbe.periodSeconds Period seconds for livenessProbe + ## @param master.livenessProbe.timeoutSeconds Timeout seconds for livenessProbe + ## @param master.livenessProbe.failureThreshold Failure threshold for livenessProbe + ## @param master.livenessProbe.successThreshold Success threshold for livenessProbe + ## + livenessProbe: + enabled: true + initialDelaySeconds: 20 + periodSeconds: 5 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + ## @param master.readinessProbe.enabled Enable readinessProbe on Redis® master nodes + ## @param master.readinessProbe.initialDelaySeconds Initial delay seconds for readinessProbe + ## @param master.readinessProbe.periodSeconds Period seconds for readinessProbe + ## @param master.readinessProbe.timeoutSeconds Timeout seconds for readinessProbe + ## @param master.readinessProbe.failureThreshold Failure threshold for readinessProbe + ## @param master.readinessProbe.successThreshold Success threshold for readinessProbe + ## + readinessProbe: + enabled: true + initialDelaySeconds: 20 + periodSeconds: 5 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 5 + ## @param master.customStartupProbe Custom startupProbe that overrides the default one + ## + customStartupProbe: {} + ## @param master.customLivenessProbe Custom livenessProbe that overrides the default one + ## + customLivenessProbe: {} + ## @param master.customReadinessProbe Custom readinessProbe that overrides the default one + ## + customReadinessProbe: {} + ## Redis® master resource requests and limits + ## ref: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/ + ## @param master.resourcesPreset Set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). This is ignored if master.resources is set (master.resources is recommended for production). + ## More information: https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15 + ## + resourcesPreset: "nano" + ## @param master.resources Set container requests and limits for different resources like CPU or memory (essential for production workloads) + ## Example: + ## resources: + ## requests: + ## cpu: 2 + ## memory: 512Mi + ## limits: + ## cpu: 3 + ## memory: 1024Mi + ## + resources: {} + ## Configure Pods Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod + ## @param master.podSecurityContext.enabled Enabled Redis® master pods' Security Context + ## @param master.podSecurityContext.fsGroupChangePolicy Set filesystem group change policy + ## @param master.podSecurityContext.sysctls Set kernel settings using the sysctl interface + ## @param master.podSecurityContext.supplementalGroups Set filesystem extra groups + ## @param master.podSecurityContext.fsGroup Set Redis® master pod's Security Context fsGroup + ## + podSecurityContext: + enabled: true + fsGroupChangePolicy: Always + sysctls: [] + supplementalGroups: [] + fsGroup: 1001 + ## Configure Container Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod + ## @param master.containerSecurityContext.enabled Enabled Redis® master containers' Security Context + ## @param master.containerSecurityContext.seLinuxOptions [object,nullable] Set SELinux options in container + ## @param master.containerSecurityContext.runAsUser Set Redis® master containers' Security Context runAsUser + ## @param master.containerSecurityContext.runAsGroup Set Redis® master containers' Security Context runAsGroup + ## @param master.containerSecurityContext.runAsNonRoot Set Redis® master containers' Security Context runAsNonRoot + ## @param master.containerSecurityContext.allowPrivilegeEscalation Is it possible to escalate Redis® pod(s) privileges + ## @param master.containerSecurityContext.readOnlyRootFilesystem Set container's Security Context read-only root filesystem + ## @param master.containerSecurityContext.seccompProfile.type Set Redis® master containers' Security Context seccompProfile + ## @param master.containerSecurityContext.capabilities.drop Set Redis® master containers' Security Context capabilities to drop + ## + containerSecurityContext: + enabled: true + seLinuxOptions: {} + runAsUser: 1001 + runAsGroup: 1001 + runAsNonRoot: true + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + seccompProfile: + type: RuntimeDefault + capabilities: + drop: ["ALL"] + ## @param master.kind Use either Deployment, StatefulSet (default) or DaemonSet + ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/ + ## + kind: StatefulSet + ## @param master.schedulerName Alternate scheduler for Redis® master pods + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + schedulerName: "" + ## @param master.updateStrategy.type Redis® master statefulset strategy type + ## @skip master.updateStrategy.rollingUpdate + ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies + ## + updateStrategy: + ## StrategyType + ## Can be set to RollingUpdate, OnDelete (statefulset), Recreate (deployment) + ## + type: RollingUpdate + ## @param master.minReadySeconds How many seconds a pod needs to be ready before killing the next, during update + ## + minReadySeconds: 0 + ## @param master.priorityClassName Redis® master pods' priorityClassName + ## + priorityClassName: "" + ## @param master.automountServiceAccountToken Mount Service Account token in pod + ## + automountServiceAccountToken: false + ## @param master.hostAliases Redis® master pods host aliases + ## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ + ## + hostAliases: [] + ## @param master.podLabels Extra labels for Redis® master pods + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + ## + podLabels: {} + ## @param master.podAnnotations Annotations for Redis® master pods + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + ## + podAnnotations: {} + ## @param master.shareProcessNamespace Share a single process namespace between all of the containers in Redis® master pods + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/share-process-namespace/ + ## + shareProcessNamespace: false + ## @param master.podAffinityPreset Pod affinity preset. Ignored if `master.affinity` is set. Allowed values: `soft` or `hard` + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## + podAffinityPreset: "" + ## @param master.podAntiAffinityPreset Pod anti-affinity preset. Ignored if `master.affinity` is set. Allowed values: `soft` or `hard` + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## + podAntiAffinityPreset: soft + ## Node master.affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity + ## + nodeAffinityPreset: + ## @param master.nodeAffinityPreset.type Node affinity preset type. Ignored if `master.affinity` is set. Allowed values: `soft` or `hard` + ## + type: "" + ## @param master.nodeAffinityPreset.key Node label key to match. Ignored if `master.affinity` is set + ## + key: "" + ## @param master.nodeAffinityPreset.values Node label values to match. Ignored if `master.affinity` is set + ## E.g. + ## values: + ## - e2e-az1 + ## - e2e-az2 + ## + values: [] + ## @param master.affinity Affinity for Redis® master pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## NOTE: `master.podAffinityPreset`, `master.podAntiAffinityPreset`, and `master.nodeAffinityPreset` will be ignored when it's set + ## + affinity: {} + ## @param master.nodeSelector Node labels for Redis® master pods assignment + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/ + ## + nodeSelector: {} + ## @param master.tolerations Tolerations for Redis® master pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + ## + tolerations: [] + ## @param master.topologySpreadConstraints Spread Constraints for Redis® master pod assignment + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ + ## E.g. + ## topologySpreadConstraints: + ## - maxSkew: 1 + ## topologyKey: node + ## whenUnsatisfiable: DoNotSchedule + ## + topologySpreadConstraints: [] + ## @param master.dnsPolicy DNS Policy for Redis® master pod + ## ref: https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/ + ## E.g. + ## dnsPolicy: ClusterFirst + ## + dnsPolicy: "" + ## @param master.dnsConfig DNS Configuration for Redis® master pod + ## ref: https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/ + ## E.g. + ## dnsConfig: + ## options: + ## - name: ndots + ## value: "4" + ## - name: single-request-reopen + ## + dnsConfig: {} + ## @param master.lifecycleHooks for the Redis® master container(s) to automate configuration before or after startup + ## + lifecycleHooks: {} + ## @param master.extraVolumes Optionally specify extra list of additional volumes for the Redis® master pod(s) + ## + extraVolumes: [] + ## @param master.extraVolumeMounts Optionally specify extra list of additional volumeMounts for the Redis® master container(s) + ## + extraVolumeMounts: [] + ## @param master.sidecars Add additional sidecar containers to the Redis® master pod(s) + ## e.g: + ## sidecars: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## ports: + ## - name: portname + ## containerPort: 1234 + ## + sidecars: [] + ## @param master.initContainers Add additional init containers to the Redis® master pod(s) + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ + ## e.g: + ## initContainers: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## command: ['sh', '-c', 'echo "hello world"'] + ## + initContainers: [] + ## Persistence parameters + ## ref: https://kubernetes.io/docs/concepts/storage/persistent-volumes/ + ## + persistence: + ## @param master.persistence.enabled Enable persistence on Redis® master nodes using Persistent Volume Claims + ## + enabled: true + ## @param master.persistence.medium Provide a medium for `emptyDir` volumes. + ## + medium: "" + ## @param master.persistence.sizeLimit Set this to enable a size limit for `emptyDir` volumes. + ## + sizeLimit: "" + ## @param master.persistence.path The path the volume will be mounted at on Redis® master containers + ## NOTE: Useful when using different Redis® images + ## + path: /data + ## @param master.persistence.subPath The subdirectory of the volume to mount on Redis® master containers + ## NOTE: Useful in dev environments + ## + subPath: "" + ## @param master.persistence.subPathExpr Used to construct the subPath subdirectory of the volume to mount on Redis® master containers + ## + subPathExpr: "" + ## @param master.persistence.storageClass Persistent Volume storage class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is set, choosing the default provisioner + ## + storageClass: "" + ## @param master.persistence.accessModes Persistent Volume access modes + ## + accessModes: + - ReadWriteOnce + ## @param master.persistence.size Persistent Volume size + ## + size: 8Gi + ## @param master.persistence.annotations Additional custom annotations for the PVC + ## + annotations: {} + ## @param master.persistence.labels Additional custom labels for the PVC + ## + labels: {} + ## @param master.persistence.selector Additional labels to match for the PVC + ## e.g: + ## selector: + ## matchLabels: + ## app: my-app + ## + selector: {} + ## @param master.persistence.dataSource Custom PVC data source + ## + dataSource: {} + ## @param master.persistence.existingClaim Use a existing PVC which must be created manually before bound + ## NOTE: requires master.persistence.enabled: true + ## + existingClaim: "" + ## persistentVolumeClaimRetentionPolicy + ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#persistentvolumeclaim-retention + ## @param master.persistentVolumeClaimRetentionPolicy.enabled Controls if and how PVCs are deleted during the lifecycle of a StatefulSet + ## @param master.persistentVolumeClaimRetentionPolicy.whenScaled Volume retention behavior when the replica count of the StatefulSet is reduced + ## @param master.persistentVolumeClaimRetentionPolicy.whenDeleted Volume retention behavior that applies when the StatefulSet is deleted + ## + persistentVolumeClaimRetentionPolicy: + enabled: false + whenScaled: Retain + whenDeleted: Retain + ## Redis® master service parameters + ## + service: + ## @param master.service.type Redis® master service type + ## + type: ClusterIP + ## @param master.service.portNames.redis Redis® master service port name + ## + portNames: + redis: "tcp-redis" + ## @param master.service.ports.redis Redis® master service port + ## + ports: + redis: 6379 + ## @param master.service.nodePorts.redis Node port for Redis® master + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## NOTE: choose port between <30000-32767> + ## + nodePorts: + redis: "" + ## @param master.service.externalTrafficPolicy Redis® master service external traffic policy + ## ref: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip + ## + externalTrafficPolicy: Cluster + ## @param master.service.extraPorts Extra ports to expose (normally used with the `sidecar` value) + ## + extraPorts: [] + ## @param master.service.internalTrafficPolicy Redis® master service internal traffic policy (requires Kubernetes v1.22 or greater to be usable) + ## ref: https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/ + ## + internalTrafficPolicy: Cluster + ## @param master.service.clusterIP Redis® master service Cluster IP + ## + clusterIP: "" + ## @param master.service.loadBalancerIP Redis® master service Load Balancer IP + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + loadBalancerIP: "" + ## @param master.service.loadBalancerClass master service Load Balancer class if service type is `LoadBalancer` (optional, cloud specific) + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-loadbalancer + ## + loadBalancerClass: "" + ## @param master.service.loadBalancerSourceRanges Redis® master service Load Balancer sources + ## https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service + ## e.g. + ## loadBalancerSourceRanges: + ## - 10.10.10.0/24 + ## + loadBalancerSourceRanges: [] + ## @param master.service.externalIPs Redis® master service External IPs + ## https://kubernetes.io/docs/concepts/services-networking/service/#external-ips + ## e.g. + ## externalIPs: + ## - 10.10.10.1 + ## - 201.22.30.1 + ## + externalIPs: [] + ## @param master.service.annotations Additional custom annotations for Redis® master service + ## + annotations: {} + ## @param master.service.sessionAffinity Session Affinity for Kubernetes service, can be "None" or "ClientIP" + ## If "ClientIP", consecutive client requests will be directed to the same Pod + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + ## + sessionAffinity: None + ## @param master.service.sessionAffinityConfig Additional settings for the sessionAffinity + ## sessionAffinityConfig: + ## clientIP: + ## timeoutSeconds: 300 + ## + sessionAffinityConfig: {} + ## @param master.terminationGracePeriodSeconds Integer setting the termination grace period for the redis-master pods + ## + terminationGracePeriodSeconds: 30 + ## ServiceAccount configuration + ## + serviceAccount: + ## @param master.serviceAccount.create Specifies whether a ServiceAccount should be created + ## + create: true + ## @param master.serviceAccount.name The name of the ServiceAccount to use. + ## If not set and create is true, a name is generated using the common.names.fullname template + ## + name: "" + ## @param master.serviceAccount.automountServiceAccountToken Whether to auto mount the service account token + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server + ## + automountServiceAccountToken: false + ## @param master.serviceAccount.annotations Additional custom annotations for the ServiceAccount + ## + annotations: {} + ## Pod Disruption Budget configuration + ## ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb + ## @param master.pdb.create Enable/disable a Pod Disruption Budget creation + ## @param master.pdb.minAvailable [object] Minimum number/percentage of pods that should remain scheduled + ## @param master.pdb.maxUnavailable [object] Maximum number/percentage of pods that may be made unavailable. Defaults to `1` if both `master.pdb.minAvailable` and `master.pdb.maxUnavailable` are empty. + ## + pdb: + create: true + minAvailable: "" + maxUnavailable: "" + ## @param master.extraPodSpec Optionally specify extra PodSpec for the Redis® master pod(s) + ## + extraPodSpec: {} + ## @param master.annotations Additional custom annotations for Redis® Master resource + ## + annotations: {} +## @section Redis® replicas configuration parameters +## +replica: + ## @param replica.kind Use either DaemonSet or StatefulSet (default) + ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/ + ## + kind: StatefulSet + ## @param replica.replicaCount Number of Redis® replicas to deploy + ## + replicaCount: 3 + ## @param replica.revisionHistoryLimit The number of old history to retain to allow rollback + ## NOTE: Explicitly setting this field to 0, will result in cleaning up all the history, breaking ability to rollback + revisionHistoryLimit: 10 + ## @param replica.configuration Configuration for Redis® replicas nodes + ## ref: https://redis.io/topics/config + ## + configuration: "" + ## @param replica.disableCommands Array with Redis® commands to disable on replicas nodes + ## Commands will be completely disabled by renaming each to an empty string. + ## ref: https://redis.io/topics/security#disabling-of-specific-commands + ## + disableCommands: + - FLUSHDB + - FLUSHALL + ## @param replica.command Override default container command (useful when using custom images) + ## + command: [] + ## @param replica.args Override default container args (useful when using custom images) + ## + args: [] + ## @param replica.enableServiceLinks Whether information about services should be injected into pod's environment variable + ## + enableServiceLinks: true + ## @param replica.preExecCmds Additional commands to run prior to starting Redis® replicas + ## + preExecCmds: [] + ## @param replica.extraFlags Array with additional command line flags for Redis® replicas + ## e.g: + ## extraFlags: + ## - "--maxmemory-policy volatile-ttl" + ## - "--repl-backlog-size 1024mb" + ## + extraFlags: [] + ## @param replica.extraEnvVars Array with extra environment variables to add to Redis® replicas nodes + ## e.g: + ## extraEnvVars: + ## - name: FOO + ## value: "bar" + ## + extraEnvVars: [] + ## @param replica.extraEnvVarsCM Name of existing ConfigMap containing extra env vars for Redis® replicas nodes + ## + extraEnvVarsCM: "" + ## @param replica.extraEnvVarsSecret Name of existing Secret containing extra env vars for Redis® replicas nodes + ## + extraEnvVarsSecret: "" + ## @param replica.externalMaster.enabled Use external master for bootstrapping + ## @param replica.externalMaster.host External master host to bootstrap from + ## @param replica.externalMaster.port Port for Redis service external master host + ## + externalMaster: + enabled: false + host: "" + port: 6379 + ## @param replica.containerPorts.redis Container port to open on Redis® replicas nodes + ## + containerPorts: + redis: 6379 + ## Configure extra options for Redis® containers' liveness and readiness probes + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes + ## @param replica.startupProbe.enabled Enable startupProbe on Redis® replicas nodes + ## @param replica.startupProbe.initialDelaySeconds Initial delay seconds for startupProbe + ## @param replica.startupProbe.periodSeconds Period seconds for startupProbe + ## @param replica.startupProbe.timeoutSeconds Timeout seconds for startupProbe + ## @param replica.startupProbe.failureThreshold Failure threshold for startupProbe + ## @param replica.startupProbe.successThreshold Success threshold for startupProbe + ## + startupProbe: + enabled: true + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 22 + ## @param replica.livenessProbe.enabled Enable livenessProbe on Redis® replicas nodes + ## @param replica.livenessProbe.initialDelaySeconds Initial delay seconds for livenessProbe + ## @param replica.livenessProbe.periodSeconds Period seconds for livenessProbe + ## @param replica.livenessProbe.timeoutSeconds Timeout seconds for livenessProbe + ## @param replica.livenessProbe.failureThreshold Failure threshold for livenessProbe + ## @param replica.livenessProbe.successThreshold Success threshold for livenessProbe + ## + livenessProbe: + enabled: true + initialDelaySeconds: 20 + periodSeconds: 5 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + ## @param replica.readinessProbe.enabled Enable readinessProbe on Redis® replicas nodes + ## @param replica.readinessProbe.initialDelaySeconds Initial delay seconds for readinessProbe + ## @param replica.readinessProbe.periodSeconds Period seconds for readinessProbe + ## @param replica.readinessProbe.timeoutSeconds Timeout seconds for readinessProbe + ## @param replica.readinessProbe.failureThreshold Failure threshold for readinessProbe + ## @param replica.readinessProbe.successThreshold Success threshold for readinessProbe + ## + readinessProbe: + enabled: true + initialDelaySeconds: 20 + periodSeconds: 5 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 5 + ## @param replica.customStartupProbe Custom startupProbe that overrides the default one + ## + customStartupProbe: {} + ## @param replica.customLivenessProbe Custom livenessProbe that overrides the default one + ## + customLivenessProbe: {} + ## @param replica.customReadinessProbe Custom readinessProbe that overrides the default one + ## + customReadinessProbe: {} + ## Redis® replicas resource requests and limits + ## ref: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/ + ## @param replica.resourcesPreset Set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). This is ignored if replica.resources is set (replica.resources is recommended for production). + ## More information: https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15 + ## + resourcesPreset: "nano" + ## @param replica.resources Set container requests and limits for different resources like CPU or memory (essential for production workloads) + ## Example: + ## resources: + ## requests: + ## cpu: 2 + ## memory: 512Mi + ## limits: + ## cpu: 3 + ## memory: 1024Mi + ## + resources: {} + ## Configure Pods Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod + ## @param replica.podSecurityContext.enabled Enabled Redis® replicas pods' Security Context + ## @param replica.podSecurityContext.fsGroupChangePolicy Set filesystem group change policy + ## @param replica.podSecurityContext.sysctls Set kernel settings using the sysctl interface + ## @param replica.podSecurityContext.supplementalGroups Set filesystem extra groups + ## @param replica.podSecurityContext.fsGroup Set Redis® replicas pod's Security Context fsGroup + ## + podSecurityContext: + enabled: true + fsGroupChangePolicy: Always + sysctls: [] + supplementalGroups: [] + fsGroup: 1001 + ## Configure Container Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod + ## @param replica.containerSecurityContext.enabled Enabled Redis® replicas containers' Security Context + ## @param replica.containerSecurityContext.seLinuxOptions [object,nullable] Set SELinux options in container + ## @param replica.containerSecurityContext.runAsUser Set Redis® replicas containers' Security Context runAsUser + ## @param replica.containerSecurityContext.runAsGroup Set Redis® replicas containers' Security Context runAsGroup + ## @param replica.containerSecurityContext.runAsNonRoot Set Redis® replicas containers' Security Context runAsNonRoot + ## @param replica.containerSecurityContext.allowPrivilegeEscalation Set Redis® replicas pod's Security Context allowPrivilegeEscalation + ## @param replica.containerSecurityContext.readOnlyRootFilesystem Set container's Security Context read-only root filesystem + ## @param replica.containerSecurityContext.seccompProfile.type Set Redis® replicas containers' Security Context seccompProfile + ## @param replica.containerSecurityContext.capabilities.drop Set Redis® replicas containers' Security Context capabilities to drop + ## + containerSecurityContext: + enabled: true + seLinuxOptions: {} + runAsUser: 1001 + runAsGroup: 1001 + runAsNonRoot: true + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + seccompProfile: + type: RuntimeDefault + capabilities: + drop: ["ALL"] + ## @param replica.schedulerName Alternate scheduler for Redis® replicas pods + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + schedulerName: "" + ## @param replica.updateStrategy.type Redis® replicas statefulset strategy type + ## @skip replica.updateStrategy.rollingUpdate + ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies + ## + updateStrategy: + ## StrategyType + ## Can be set to RollingUpdate, OnDelete (statefulset), Recreate (deployment) + ## + type: RollingUpdate + ## @param replica.minReadySeconds How many seconds a pod needs to be ready before killing the next, during update + ## + minReadySeconds: 0 + ## @param replica.priorityClassName Redis® replicas pods' priorityClassName + ## + priorityClassName: "" + ## @param replica.podManagementPolicy podManagementPolicy to manage scaling operation of %%MAIN_CONTAINER_NAME%% pods + ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#pod-management-policies + ## + podManagementPolicy: "" + ## @param replica.automountServiceAccountToken Mount Service Account token in pod + ## + automountServiceAccountToken: false + ## @param replica.hostAliases Redis® replicas pods host aliases + ## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ + ## + hostAliases: [] + ## @param replica.podLabels Extra labels for Redis® replicas pods + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + ## + podLabels: {} + ## @param replica.podAnnotations Annotations for Redis® replicas pods + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + ## + podAnnotations: {} + ## @param replica.shareProcessNamespace Share a single process namespace between all of the containers in Redis® replicas pods + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/share-process-namespace/ + ## + shareProcessNamespace: false + ## @param replica.podAffinityPreset Pod affinity preset. Ignored if `replica.affinity` is set. Allowed values: `soft` or `hard` + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## + podAffinityPreset: "" + ## @param replica.podAntiAffinityPreset Pod anti-affinity preset. Ignored if `replica.affinity` is set. Allowed values: `soft` or `hard` + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## + podAntiAffinityPreset: soft + ## Node affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity + ## + nodeAffinityPreset: + ## @param replica.nodeAffinityPreset.type Node affinity preset type. Ignored if `replica.affinity` is set. Allowed values: `soft` or `hard` + ## + type: "" + ## @param replica.nodeAffinityPreset.key Node label key to match. Ignored if `replica.affinity` is set + ## + key: "" + ## @param replica.nodeAffinityPreset.values Node label values to match. Ignored if `replica.affinity` is set + ## E.g. + ## values: + ## - e2e-az1 + ## - e2e-az2 + ## + values: [] + ## @param replica.affinity Affinity for Redis® replicas pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## NOTE: `replica.podAffinityPreset`, `replica.podAntiAffinityPreset`, and `replica.nodeAffinityPreset` will be ignored when it's set + ## + affinity: {} + ## @param replica.nodeSelector Node labels for Redis® replicas pods assignment + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/ + ## + nodeSelector: {} + ## @param replica.tolerations Tolerations for Redis® replicas pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + ## + tolerations: [] + ## @param replica.topologySpreadConstraints Spread Constraints for Redis® replicas pod assignment + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ + ## E.g. + ## topologySpreadConstraints: + ## - maxSkew: 1 + ## topologyKey: node + ## whenUnsatisfiable: DoNotSchedule + ## + topologySpreadConstraints: [] + ## @param replica.dnsPolicy DNS Policy for Redis® replica pods + ## ref: https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/ + ## E.g. + ## dnsPolicy: ClusterFirst + ## + dnsPolicy: "" + ## @param replica.dnsConfig DNS Configuration for Redis® replica pods + ## ref: https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/ + ## E.g. + ## dnsConfig: + ## options: + ## - name: ndots + ## value: "4" + ## - name: single-request-reopen + ## + dnsConfig: {} + ## @param replica.lifecycleHooks for the Redis® replica container(s) to automate configuration before or after startup + ## + lifecycleHooks: {} + ## @param replica.extraVolumes Optionally specify extra list of additional volumes for the Redis® replicas pod(s) + ## + extraVolumes: [] + ## @param replica.extraVolumeMounts Optionally specify extra list of additional volumeMounts for the Redis® replicas container(s) + ## + extraVolumeMounts: [] + ## @param replica.sidecars Add additional sidecar containers to the Redis® replicas pod(s) + ## e.g: + ## sidecars: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## ports: + ## - name: portname + ## containerPort: 1234 + ## + sidecars: [] + ## @param replica.initContainers Add additional init containers to the Redis® replicas pod(s) + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ + ## e.g: + ## initContainers: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## command: ['sh', '-c', 'echo "hello world"'] + ## + initContainers: [] + ## Persistence Parameters + ## ref: https://kubernetes.io/docs/concepts/storage/persistent-volumes/ + ## + persistence: + ## @param replica.persistence.enabled Enable persistence on Redis® replicas nodes using Persistent Volume Claims + ## + enabled: true + ## @param replica.persistence.medium Provide a medium for `emptyDir` volumes. + ## + medium: "" + ## @param replica.persistence.sizeLimit Set this to enable a size limit for `emptyDir` volumes. + ## + sizeLimit: "" + ## @param replica.persistence.path The path the volume will be mounted at on Redis® replicas containers + ## NOTE: Useful when using different Redis® images + ## + path: /data + ## @param replica.persistence.subPath The subdirectory of the volume to mount on Redis® replicas containers + ## NOTE: Useful in dev environments + ## + subPath: "" + ## @param replica.persistence.subPathExpr Used to construct the subPath subdirectory of the volume to mount on Redis® replicas containers + ## + subPathExpr: "" + ## @param replica.persistence.storageClass Persistent Volume storage class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is set, choosing the default provisioner + ## + storageClass: "" + ## @param replica.persistence.accessModes Persistent Volume access modes + ## + accessModes: + - ReadWriteOnce + ## @param replica.persistence.size Persistent Volume size + ## + size: 8Gi + ## @param replica.persistence.annotations Additional custom annotations for the PVC + ## + annotations: {} + ## @param replica.persistence.labels Additional custom labels for the PVC + ## + labels: {} + ## @param replica.persistence.selector Additional labels to match for the PVC + ## e.g: + ## selector: + ## matchLabels: + ## app: my-app + ## + selector: {} + ## @param replica.persistence.dataSource Custom PVC data source + ## + dataSource: {} + ## @param replica.persistence.existingClaim Use a existing PVC which must be created manually before bound + ## NOTE: requires replica.persistence.enabled: true + ## + existingClaim: "" + ## persistentVolumeClaimRetentionPolicy + ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#persistentvolumeclaim-retention + ## @param replica.persistentVolumeClaimRetentionPolicy.enabled Controls if and how PVCs are deleted during the lifecycle of a StatefulSet + ## @param replica.persistentVolumeClaimRetentionPolicy.whenScaled Volume retention behavior when the replica count of the StatefulSet is reduced + ## @param replica.persistentVolumeClaimRetentionPolicy.whenDeleted Volume retention behavior that applies when the StatefulSet is deleted + ## + persistentVolumeClaimRetentionPolicy: + enabled: false + whenScaled: Retain + whenDeleted: Retain + ## Redis® replicas service parameters + ## + service: + ## @param replica.service.type Redis® replicas service type + ## + type: ClusterIP + ## @param replica.service.ports.redis Redis® replicas service port + ## + ports: + redis: 6379 + ## @param replica.service.nodePorts.redis Node port for Redis® replicas + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## NOTE: choose port between <30000-32767> + ## + nodePorts: + redis: "" + ## @param replica.service.externalTrafficPolicy Redis® replicas service external traffic policy + ## ref: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip + ## + externalTrafficPolicy: Cluster + ## @param replica.service.internalTrafficPolicy Redis® replicas service internal traffic policy (requires Kubernetes v1.22 or greater to be usable) + ## ref: https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/ + ## + internalTrafficPolicy: Cluster + ## @param replica.service.extraPorts Extra ports to expose (normally used with the `sidecar` value) + ## + extraPorts: [] + ## @param replica.service.clusterIP Redis® replicas service Cluster IP + ## + clusterIP: "" + ## @param replica.service.loadBalancerIP Redis® replicas service Load Balancer IP + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + loadBalancerIP: "" + ## @param replica.service.loadBalancerClass replicas service Load Balancer class if service type is `LoadBalancer` (optional, cloud specific) + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-loadbalancer + ## + loadBalancerClass: "" + ## @param replica.service.loadBalancerSourceRanges Redis® replicas service Load Balancer sources + ## https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service + ## e.g. + ## loadBalancerSourceRanges: + ## - 10.10.10.0/24 + ## + loadBalancerSourceRanges: [] + ## @param replica.service.annotations Additional custom annotations for Redis® replicas service + ## + annotations: {} + ## @param replica.service.sessionAffinity Session Affinity for Kubernetes service, can be "None" or "ClientIP" + ## If "ClientIP", consecutive client requests will be directed to the same Pod + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + ## + sessionAffinity: None + ## @param replica.service.sessionAffinityConfig Additional settings for the sessionAffinity + ## sessionAffinityConfig: + ## clientIP: + ## timeoutSeconds: 300 + ## + sessionAffinityConfig: {} + ## @param replica.terminationGracePeriodSeconds Integer setting the termination grace period for the redis-replicas pods + ## + terminationGracePeriodSeconds: 30 + ## Autoscaling configuration + ## + autoscaling: + ## @param replica.autoscaling.enabled Enable replica autoscaling settings + ## + enabled: false + ## @param replica.autoscaling.minReplicas Minimum replicas for the pod autoscaling + ## + minReplicas: 1 + ## @param replica.autoscaling.maxReplicas Maximum replicas for the pod autoscaling + ## + maxReplicas: 11 + ## @param replica.autoscaling.targetCPU Percentage of CPU to consider when autoscaling + ## + targetCPU: "" + ## @param replica.autoscaling.targetMemory Percentage of Memory to consider when autoscaling + ## + targetMemory: "" + ## ServiceAccount configuration + ## + serviceAccount: + ## @param replica.serviceAccount.create Specifies whether a ServiceAccount should be created + ## + create: true + ## @param replica.serviceAccount.name The name of the ServiceAccount to use. + ## If not set and create is true, a name is generated using the common.names.fullname template + ## + name: "" + ## @param replica.serviceAccount.automountServiceAccountToken Whether to auto mount the service account token + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server + ## + automountServiceAccountToken: false + ## @param replica.serviceAccount.annotations Additional custom annotations for the ServiceAccount + ## + annotations: {} + ## Pod Disruption Budget configuration + ## ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb + ## @param replica.pdb.create Enable/disable a Pod Disruption Budget creation + ## @param replica.pdb.minAvailable [object] Minimum number/percentage of pods that should remain scheduled + ## @param replica.pdb.maxUnavailable [object] Maximum number/percentage of pods that may be made unavailable. Defaults to `1` if both `replica.pdb.minAvailable` and `replica.pdb.maxUnavailable` are empty. + ## + pdb: + create: true + minAvailable: "" + maxUnavailable: "" + ## @param replica.extraPodSpec Optionally specify extra PodSpec for the Redis® replicas pod(s) + ## + extraPodSpec: {} + ## @param replica.annotations Additional custom annotations for Redis® replicas resource + ## + annotations: {} +## @section Redis® Sentinel configuration parameters +## + +sentinel: + ## @param sentinel.enabled Use Redis® Sentinel on Redis® pods. + ## IMPORTANT: this will disable the master and replicas services and + ## create a single Redis® service exposing both the Redis and Sentinel ports + ## + enabled: false + ## Bitnami Redis® Sentinel image version + ## ref: https://hub.docker.com/r/bitnami/redis-sentinel/tags/ + ## @param sentinel.image.registry [default: REGISTRY_NAME] Redis® Sentinel image registry + ## @param sentinel.image.repository [default: REPOSITORY_NAME/redis-sentinel] Redis® Sentinel image repository + ## @skip sentinel.image.tag Redis® Sentinel image tag (immutable tags are recommended) + ## @param sentinel.image.digest Redis® Sentinel image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag + ## @param sentinel.image.pullPolicy Redis® Sentinel image pull policy + ## @param sentinel.image.pullSecrets Redis® Sentinel image pull secrets + ## @param sentinel.image.debug Enable image debug mode + ## + image: + registry: docker.io + repository: bitnami/redis-sentinel + tag: 7.4.2-debian-12-r0 + digest: "" + ## Specify a imagePullPolicy + ## ref: https://kubernetes.io/docs/concepts/containers/images/#pre-pulled-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## e.g: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + pullSecrets: [] + ## Enable debug mode + ## + debug: false + ## @param sentinel.annotations Additional custom annotations for Redis® Sentinel resource + ## + annotations: {} + ## @param sentinel.masterSet Master set name + ## + masterSet: mymaster + ## @param sentinel.quorum Sentinel Quorum + ## + quorum: 2 + ## @param sentinel.getMasterTimeout Amount of time to allow before get_sentinel_master_info() times out. + ## + getMasterTimeout: 90 + ## @param sentinel.automateClusterRecovery Automate cluster recovery in cases where the last replica is not considered a good replica and Sentinel won't automatically failover to it. + ## This also prevents any new replica from starting until the last remaining replica is elected as master to guarantee that it is the one to be elected by Sentinel, and not a newly started replica with no data. + ## NOTE: This feature requires a "downAfterMilliseconds" value less or equal to 2000. + ## + automateClusterRecovery: false + ## @param sentinel.redisShutdownWaitFailover Whether the Redis® master container waits for the failover at shutdown (in addition to the Redis® Sentinel container). + ## + redisShutdownWaitFailover: true + ## Sentinel timing restrictions + ## @param sentinel.downAfterMilliseconds Timeout for detecting a Redis® node is down + ## @param sentinel.failoverTimeout Timeout for performing a election failover + ## + downAfterMilliseconds: 60000 + failoverTimeout: 180000 + ## @param sentinel.parallelSyncs Number of replicas that can be reconfigured in parallel to use the new master after a failover + ## + parallelSyncs: 1 + ## @param sentinel.configuration Configuration for Redis® Sentinel nodes + ## ref: https://redis.io/topics/sentinel + ## + configuration: "" + ## @param sentinel.command Override default container command (useful when using custom images) + ## + command: [] + ## @param sentinel.args Override default container args (useful when using custom images) + ## + args: [] + ## @param sentinel.enableServiceLinks Whether information about services should be injected into pod's environment variable + ## + enableServiceLinks: true + ## @param sentinel.preExecCmds Additional commands to run prior to starting Redis® Sentinel + ## + preExecCmds: [] + ## @param sentinel.extraEnvVars Array with extra environment variables to add to Redis® Sentinel nodes + ## e.g: + ## extraEnvVars: + ## - name: FOO + ## value: "bar" + ## + extraEnvVars: [] + ## @param sentinel.extraEnvVarsCM Name of existing ConfigMap containing extra env vars for Redis® Sentinel nodes + ## + extraEnvVarsCM: "" + ## @param sentinel.extraEnvVarsSecret Name of existing Secret containing extra env vars for Redis® Sentinel nodes + ## + extraEnvVarsSecret: "" + ## @param sentinel.externalMaster.enabled Use external master for bootstrapping + ## @param sentinel.externalMaster.host External master host to bootstrap from + ## @param sentinel.externalMaster.port Port for Redis service external master host + ## + externalMaster: + enabled: false + host: "" + port: 6379 + ## @param sentinel.containerPorts.sentinel Container port to open on Redis® Sentinel nodes + ## + containerPorts: + sentinel: 26379 + ## Configure extra options for Redis® containers' liveness and readiness probes + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes + ## @param sentinel.startupProbe.enabled Enable startupProbe on Redis® Sentinel nodes + ## @param sentinel.startupProbe.initialDelaySeconds Initial delay seconds for startupProbe + ## @param sentinel.startupProbe.periodSeconds Period seconds for startupProbe + ## @param sentinel.startupProbe.timeoutSeconds Timeout seconds for startupProbe + ## @param sentinel.startupProbe.failureThreshold Failure threshold for startupProbe + ## @param sentinel.startupProbe.successThreshold Success threshold for startupProbe + ## + startupProbe: + enabled: true + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 22 + ## @param sentinel.livenessProbe.enabled Enable livenessProbe on Redis® Sentinel nodes + ## @param sentinel.livenessProbe.initialDelaySeconds Initial delay seconds for livenessProbe + ## @param sentinel.livenessProbe.periodSeconds Period seconds for livenessProbe + ## @param sentinel.livenessProbe.timeoutSeconds Timeout seconds for livenessProbe + ## @param sentinel.livenessProbe.failureThreshold Failure threshold for livenessProbe + ## @param sentinel.livenessProbe.successThreshold Success threshold for livenessProbe + ## + livenessProbe: + enabled: true + initialDelaySeconds: 20 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 6 + ## @param sentinel.readinessProbe.enabled Enable readinessProbe on Redis® Sentinel nodes + ## @param sentinel.readinessProbe.initialDelaySeconds Initial delay seconds for readinessProbe + ## @param sentinel.readinessProbe.periodSeconds Period seconds for readinessProbe + ## @param sentinel.readinessProbe.timeoutSeconds Timeout seconds for readinessProbe + ## @param sentinel.readinessProbe.failureThreshold Failure threshold for readinessProbe + ## @param sentinel.readinessProbe.successThreshold Success threshold for readinessProbe + ## + readinessProbe: + enabled: true + initialDelaySeconds: 20 + periodSeconds: 5 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 6 + ## @param sentinel.customStartupProbe Custom startupProbe that overrides the default one + ## + customStartupProbe: {} + ## @param sentinel.customLivenessProbe Custom livenessProbe that overrides the default one + ## + customLivenessProbe: {} + ## @param sentinel.customReadinessProbe Custom readinessProbe that overrides the default one + ## + customReadinessProbe: {} + ## Persistence parameters + ## ref: https://kubernetes.io/docs/concepts/storage/persistent-volumes/ + ## + persistence: + ## @param sentinel.persistence.enabled Enable persistence on Redis® sentinel nodes using Persistent Volume Claims (Experimental) + ## + enabled: false + ## @param sentinel.persistence.storageClass Persistent Volume storage class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is set, choosing the default provisioner + ## + storageClass: "" + ## @param sentinel.persistence.accessModes Persistent Volume access modes + ## + accessModes: + - ReadWriteOnce + ## @param sentinel.persistence.size Persistent Volume size + ## + size: 100Mi + ## @param sentinel.persistence.annotations Additional custom annotations for the PVC + ## + annotations: {} + ## @param sentinel.persistence.labels Additional custom labels for the PVC + ## + labels: {} + ## @param sentinel.persistence.selector Additional labels to match for the PVC + ## e.g: + ## selector: + ## matchLabels: + ## app: my-app + ## + selector: {} + ## @param sentinel.persistence.dataSource Custom PVC data source + ## + dataSource: {} + ## @param sentinel.persistence.medium Provide a medium for `emptyDir` volumes. + ## + medium: "" + ## @param sentinel.persistence.sizeLimit Set this to enable a size limit for `emptyDir` volumes. + ## + sizeLimit: "" + ## persistentVolumeClaimRetentionPolicy + ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#persistentvolumeclaim-retention + ## @param sentinel.persistentVolumeClaimRetentionPolicy.enabled Controls if and how PVCs are deleted during the lifecycle of a StatefulSet + ## @param sentinel.persistentVolumeClaimRetentionPolicy.whenScaled Volume retention behavior when the replica count of the StatefulSet is reduced + ## @param sentinel.persistentVolumeClaimRetentionPolicy.whenDeleted Volume retention behavior that applies when the StatefulSet is deleted + ## + persistentVolumeClaimRetentionPolicy: + enabled: false + whenScaled: Retain + whenDeleted: Retain + ## Redis® Sentinel resource requests and limits + ## ref: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/ + ## @param sentinel.resourcesPreset Set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). This is ignored if sentinel.resources is set (sentinel.resources is recommended for production). + ## More information: https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15 + ## + resourcesPreset: "nano" + ## @param sentinel.resources Set container requests and limits for different resources like CPU or memory (essential for production workloads) + ## Example: + ## resources: + ## requests: + ## cpu: 2 + ## memory: 512Mi + ## limits: + ## cpu: 3 + ## memory: 1024Mi + ## + resources: {} + ## Configure Container Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod + ## @param sentinel.containerSecurityContext.enabled Enabled Redis® Sentinel containers' Security Context + ## @param sentinel.containerSecurityContext.seLinuxOptions [object,nullable] Set SELinux options in container + ## @param sentinel.containerSecurityContext.runAsUser Set Redis® Sentinel containers' Security Context runAsUser + ## @param sentinel.containerSecurityContext.runAsGroup Set Redis® Sentinel containers' Security Context runAsGroup + ## @param sentinel.containerSecurityContext.runAsNonRoot Set Redis® Sentinel containers' Security Context runAsNonRoot + ## @param sentinel.containerSecurityContext.readOnlyRootFilesystem Set container's Security Context read-only root filesystem + ## @param sentinel.containerSecurityContext.allowPrivilegeEscalation Set Redis® Sentinel containers' Security Context allowPrivilegeEscalation + ## @param sentinel.containerSecurityContext.seccompProfile.type Set Redis® Sentinel containers' Security Context seccompProfile + ## @param sentinel.containerSecurityContext.capabilities.drop Set Redis® Sentinel containers' Security Context capabilities to drop + ## + containerSecurityContext: + enabled: true + seLinuxOptions: {} + runAsUser: 1001 + runAsGroup: 1001 + runAsNonRoot: true + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + seccompProfile: + type: RuntimeDefault + capabilities: + drop: ["ALL"] + ## @param sentinel.lifecycleHooks for the Redis® sentinel container(s) to automate configuration before or after startup + ## + lifecycleHooks: {} + ## @param sentinel.extraVolumes Optionally specify extra list of additional volumes for the Redis® Sentinel + ## + extraVolumes: [] + ## @param sentinel.extraVolumeMounts Optionally specify extra list of additional volumeMounts for the Redis® Sentinel container(s) + ## + extraVolumeMounts: [] + ## Redis® Sentinel service parameters + ## Note: values passed in this section also configure the master service, unless the sentinel.masterService is explicitly overridden. + service: + ## @param sentinel.service.type Redis® Sentinel service type + ## + type: ClusterIP + ## @param sentinel.service.ports.redis Redis® service port for Redis® + ## @param sentinel.service.ports.sentinel Redis® service port for Redis® Sentinel + ## + ports: + redis: 6379 + sentinel: 26379 + ## @param sentinel.service.nodePorts.redis Node port for Redis® + ## @param sentinel.service.nodePorts.sentinel Node port for Sentinel + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## NOTE: choose port between <30000-32767> + ## NOTE: By leaving these values blank, they will be generated by ports-configmap + ## If setting manually, please leave at least replica.replicaCount + 1 in between sentinel.service.nodePorts.redis and sentinel.service.nodePorts.sentinel to take into account the ports that will be created while incrementing that base port + ## + nodePorts: + redis: "" + sentinel: "" + ## @param sentinel.service.externalTrafficPolicy Redis® Sentinel service external traffic policy + ## ref: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip + ## + externalTrafficPolicy: Cluster + ## @param sentinel.service.extraPorts Extra ports to expose (normally used with the `sidecar` value) + ## + extraPorts: [] + ## @param sentinel.service.clusterIP Redis® Sentinel service Cluster IP + ## + clusterIP: "" + ## @param sentinel.service.createMaster Enable master service pointing to the current master (experimental) + ## NOTE: rbac.create need to be set to true + ## + createMaster: false + + ## @param sentinel.service.loadBalancerIP Redis® Sentinel service Load Balancer IP + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + loadBalancerIP: "" + ## @param sentinel.service.loadBalancerClass sentinel service Load Balancer class if service type is `LoadBalancer` (optional, cloud specific) + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-loadbalancer + ## + loadBalancerClass: "" + ## @param sentinel.service.loadBalancerSourceRanges Redis® Sentinel service Load Balancer sources + ## https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service + ## e.g. + ## loadBalancerSourceRanges: + ## - 10.10.10.0/24 + ## + loadBalancerSourceRanges: [] + ## @param sentinel.service.annotations Additional custom annotations for Redis® Sentinel service + ## + annotations: {} + ## @param sentinel.service.sessionAffinity Session Affinity for Kubernetes service, can be "None" or "ClientIP" + ## If "ClientIP", consecutive client requests will be directed to the same Pod + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + ## + sessionAffinity: None + ## @param sentinel.service.sessionAffinityConfig Additional settings for the sessionAffinity + ## sessionAffinityConfig: + ## clientIP: + ## timeoutSeconds: 300 + ## + sessionAffinityConfig: {} + ## Headless service properties + ## + headless: + ## @param sentinel.service.headless.annotations Annotations for the headless service. + ## + annotations: {} + ## @param sentinel.service.headless.extraPorts Optionally specify extra ports to expose for the headless service. + ## Example: + ## extraPorts: + ## - name: my-custom-port + ## port: 12345 + ## protocol: TCP + ## targetPort: 12345 + ## + extraPorts: [] + ## Redis® master service parameters + ## + masterService: + ## @param sentinel.masterService.enabled Enable master service pointing to the current master (experimental) + ## NOTE: rbac.create need to be set to true + ## + enabled: false + ## @param sentinel.masterService.type Redis® Sentinel master service type + ## + type: ClusterIP + ## @param sentinel.masterService.ports.redis Redis® service port for Redis® + ## + ports: + redis: 6379 + ## @param sentinel.masterService.nodePorts.redis Node port for Redis® + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## NOTE: choose port between <30000-32767> + ## NOTE: By leaving these values blank, they will be generated by ports-configmap + ## If setting manually, please leave at least replica.replicaCount + 1 in between sentinel.service.nodePorts.redis and sentinel.service.nodePorts.sentinel to take into account the ports that will be created while incrementing that base port + ## + nodePorts: + redis: "" + ## @param sentinel.masterService.externalTrafficPolicy Redis® master service external traffic policy + ## ref: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip + ## + externalTrafficPolicy: "" + ## @param sentinel.masterService.extraPorts Extra ports to expose (normally used with the `sidecar` value) + ## + extraPorts: [] + ## @param sentinel.masterService.clusterIP Redis® master service Cluster IP + ## + clusterIP: "" + ## @param sentinel.masterService.loadBalancerIP Redis® master service Load Balancer IP + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + loadBalancerIP: "" + ## @param sentinel.masterService.loadBalancerClass master service Load Balancer class if service type is `LoadBalancer` (optional, cloud specific) + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-loadbalancer + ## + loadBalancerClass: "" + ## @param sentinel.masterService.loadBalancerSourceRanges Redis® master service Load Balancer sources + ## https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service + ## e.g. + ## loadBalancerSourceRanges: + ## - 10.10.10.0/24 + ## + loadBalancerSourceRanges: [] + ## @param sentinel.masterService.annotations Additional custom annotations for Redis® master service + ## + annotations: {} + ## @param sentinel.masterService.sessionAffinity Session Affinity for Kubernetes service, can be "None" or "ClientIP" + ## If "ClientIP", consecutive client requests will be directed to the same Pod + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + ## + sessionAffinity: None + ## @param sentinel.masterService.sessionAffinityConfig Additional settings for the sessionAffinity + ## sessionAffinityConfig: + ## clientIP: + ## timeoutSeconds: 300 + ## + sessionAffinityConfig: {} + ## @param sentinel.terminationGracePeriodSeconds Integer setting the termination grace period for the redis-node pods + ## + terminationGracePeriodSeconds: 30 + ## @param sentinel.extraPodSpec Optionally specify extra PodSpec for the Redis® Sentinel pod(s) + ## + extraPodSpec: {} +## @section Other Parameters +## + +## @param serviceBindings.enabled Create secret for service binding (Experimental) +## Ref: https://servicebinding.io/service-provider/ +## +serviceBindings: + enabled: false +## Network Policy configuration +## ref: https://kubernetes.io/docs/concepts/services-networking/network-policies/ +## +networkPolicy: + ## @param networkPolicy.enabled Enable creation of NetworkPolicy resources + ## + enabled: true + ## @param networkPolicy.allowExternal Don't require client label for connections + ## When set to false, only pods with the correct client label will have network access to the ports + ## Redis® is listening on. When true, Redis® will accept connections from any source + ## (with the correct destination port). + ## + allowExternal: true + ## @param networkPolicy.allowExternalEgress Allow the pod to access any range of port and all destinations. + ## + allowExternalEgress: true + ## @param networkPolicy.extraIngress Add extra ingress rules to the NetworkPolicy + ## e.g: + ## extraIngress: + ## - ports: + ## - port: 1234 + ## from: + ## - podSelector: + ## - matchLabels: + ## - role: frontend + ## - podSelector: + ## - matchExpressions: + ## - key: role + ## operator: In + ## values: + ## - frontend + ## + extraIngress: [] + ## @param networkPolicy.extraEgress Add extra egress rules to the NetworkPolicy + ## e.g: + ## extraEgress: + ## - ports: + ## - port: 1234 + ## to: + ## - podSelector: + ## - matchLabels: + ## - role: frontend + ## - podSelector: + ## - matchExpressions: + ## - key: role + ## operator: In + ## values: + ## - frontend + ## + extraEgress: [] + ## @param networkPolicy.ingressNSMatchLabels Labels to match to allow traffic from other namespaces + ## @param networkPolicy.ingressNSPodMatchLabels Pod labels to match to allow traffic from other namespaces + ## + ingressNSMatchLabels: {} + ingressNSPodMatchLabels: {} + metrics: + ## @param networkPolicy.metrics.allowExternal Don't require client label for connections for metrics endpoint + ## When set to false, only pods with the correct client label will have network access to the metrics port + ## + allowExternal: true + ## @param networkPolicy.metrics.ingressNSMatchLabels Labels to match to allow traffic from other namespaces to metrics endpoint + ## @param networkPolicy.metrics.ingressNSPodMatchLabels Pod labels to match to allow traffic from other namespaces to metrics endpoint + ## + ingressNSMatchLabels: {} + ingressNSPodMatchLabels: {} +## PodSecurityPolicy configuration +## ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/ +## +podSecurityPolicy: + ## @param podSecurityPolicy.create Whether to create a PodSecurityPolicy. WARNING: PodSecurityPolicy is deprecated in Kubernetes v1.21 or later, unavailable in v1.25 or later + ## + create: false + ## @param podSecurityPolicy.enabled Enable PodSecurityPolicy's RBAC rules + ## + enabled: false +## RBAC configuration +## +rbac: + ## @param rbac.create Specifies whether RBAC resources should be created + ## + create: false + ## @param rbac.rules Custom RBAC rules to set + ## e.g: + ## rules: + ## - apiGroups: + ## - "" + ## resources: + ## - pods + ## verbs: + ## - get + ## - list + ## + rules: [] +## ServiceAccount configuration +## +serviceAccount: + ## @param serviceAccount.create Specifies whether a ServiceAccount should be created + ## + create: true + ## @param serviceAccount.name The name of the ServiceAccount to use. + ## If not set and create is true, a name is generated using the common.names.fullname template + ## + name: "" + ## @param serviceAccount.automountServiceAccountToken Whether to auto mount the service account token + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server + ## + automountServiceAccountToken: false + ## @param serviceAccount.annotations Additional custom annotations for the ServiceAccount + ## + annotations: {} +## Redis® Pod Disruption Budget configuration +## ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb/ +## @param pdb DEPRECATED Please use `master.pdb` and `replica.pdb` values instead +## +pdb: {} +## TLS configuration +## +tls: + ## @param tls.enabled Enable TLS traffic + ## + enabled: false + ## @param tls.authClients Require clients to authenticate + ## + authClients: true + ## @param tls.autoGenerated Enable autogenerated certificates + ## + autoGenerated: false + ## @param tls.existingSecret The name of the existing secret that contains the TLS certificates + ## + existingSecret: "" + ## @param tls.certificatesSecret DEPRECATED. Use existingSecret instead. + ## + certificatesSecret: "" + ## @param tls.certFilename Certificate filename + ## + certFilename: "" + ## @param tls.certKeyFilename Certificate Key filename + ## + certKeyFilename: "" + ## @param tls.certCAFilename CA Certificate filename + ## + certCAFilename: "" + ## @param tls.dhParamsFilename File containing DH params (in order to support DH based ciphers) + ## + dhParamsFilename: "" +## @section Metrics Parameters +## +metrics: + ## @param metrics.enabled Start a sidecar prometheus exporter to expose Redis® metrics + ## + enabled: false + ## Bitnami Redis® Exporter image + ## ref: https://hub.docker.com/r/bitnami/redis-exporter/tags/ + ## @param metrics.image.registry [default: REGISTRY_NAME] Redis® Exporter image registry + ## @param metrics.image.repository [default: REPOSITORY_NAME/redis-exporter] Redis® Exporter image repository + ## @skip metrics.image.tag Redis® Exporter image tag (immutable tags are recommended) + ## @param metrics.image.digest Redis® Exporter image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag + ## @param metrics.image.pullPolicy Redis® Exporter image pull policy + ## @param metrics.image.pullSecrets Redis® Exporter image pull secrets + ## + image: + registry: docker.io + repository: bitnami/redis-exporter + tag: 1.67.0-debian-12-r0 + digest: "" + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## e.g: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + pullSecrets: [] + ## @param metrics.containerPorts.http Metrics HTTP container port + ## + containerPorts: + http: 9121 + ## Configure extra options for Redis® containers' liveness, readiness & startup probes + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ + ## @param metrics.startupProbe.enabled Enable startupProbe on Redis® replicas nodes + ## @param metrics.startupProbe.initialDelaySeconds Initial delay seconds for startupProbe + ## @param metrics.startupProbe.periodSeconds Period seconds for startupProbe + ## @param metrics.startupProbe.timeoutSeconds Timeout seconds for startupProbe + ## @param metrics.startupProbe.failureThreshold Failure threshold for startupProbe + ## @param metrics.startupProbe.successThreshold Success threshold for startupProbe + ## + startupProbe: + enabled: false + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + ## @param metrics.livenessProbe.enabled Enable livenessProbe on Redis® replicas nodes + ## @param metrics.livenessProbe.initialDelaySeconds Initial delay seconds for livenessProbe + ## @param metrics.livenessProbe.periodSeconds Period seconds for livenessProbe + ## @param metrics.livenessProbe.timeoutSeconds Timeout seconds for livenessProbe + ## @param metrics.livenessProbe.failureThreshold Failure threshold for livenessProbe + ## @param metrics.livenessProbe.successThreshold Success threshold for livenessProbe + ## + livenessProbe: + enabled: true + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + ## @param metrics.readinessProbe.enabled Enable readinessProbe on Redis® replicas nodes + ## @param metrics.readinessProbe.initialDelaySeconds Initial delay seconds for readinessProbe + ## @param metrics.readinessProbe.periodSeconds Period seconds for readinessProbe + ## @param metrics.readinessProbe.timeoutSeconds Timeout seconds for readinessProbe + ## @param metrics.readinessProbe.failureThreshold Failure threshold for readinessProbe + ## @param metrics.readinessProbe.successThreshold Success threshold for readinessProbe + ## + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 3 + ## @param metrics.customStartupProbe Custom startupProbe that overrides the default one + ## + customStartupProbe: {} + ## @param metrics.customLivenessProbe Custom livenessProbe that overrides the default one + ## + customLivenessProbe: {} + ## @param metrics.customReadinessProbe Custom readinessProbe that overrides the default one + ## + customReadinessProbe: {} + ## @param metrics.command Override default metrics container init command (useful when using custom images) + ## + command: [] + ## @param metrics.redisTargetHost A way to specify an alternative Redis® hostname + ## Useful for certificate CN/SAN matching + ## + redisTargetHost: "localhost" + ## @param metrics.extraArgs Extra arguments for Redis® exporter, for example: + ## e.g.: + ## extraArgs: + ## check-keys: myKey,myOtherKey + ## + extraArgs: {} + ## @param metrics.extraEnvVars Array with extra environment variables to add to Redis® exporter + ## e.g: + ## extraEnvVars: + ## - name: FOO + ## value: "bar" + ## + extraEnvVars: [] + ## Configure Container Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod + ## @param metrics.containerSecurityContext.enabled Enabled Redis® exporter containers' Security Context + ## @param metrics.containerSecurityContext.seLinuxOptions [object,nullable] Set SELinux options in container + ## @param metrics.containerSecurityContext.runAsUser Set Redis® exporter containers' Security Context runAsUser + ## @param metrics.containerSecurityContext.runAsGroup Set Redis® exporter containers' Security Context runAsGroup + ## @param metrics.containerSecurityContext.runAsNonRoot Set Redis® exporter containers' Security Context runAsNonRoot + ## @param metrics.containerSecurityContext.allowPrivilegeEscalation Set Redis® exporter containers' Security Context allowPrivilegeEscalation + ## @param metrics.containerSecurityContext.readOnlyRootFilesystem Set container's Security Context read-only root filesystem + ## @param metrics.containerSecurityContext.seccompProfile.type Set Redis® exporter containers' Security Context seccompProfile + ## @param metrics.containerSecurityContext.capabilities.drop Set Redis® exporter containers' Security Context capabilities to drop + ## + containerSecurityContext: + enabled: true + seLinuxOptions: {} + runAsUser: 1001 + runAsGroup: 1001 + runAsNonRoot: true + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + seccompProfile: + type: RuntimeDefault + capabilities: + drop: ["ALL"] + ## @param metrics.extraVolumes Optionally specify extra list of additional volumes for the Redis® metrics sidecar + ## + extraVolumes: [] + ## @param metrics.extraVolumeMounts Optionally specify extra list of additional volumeMounts for the Redis® metrics sidecar + ## + extraVolumeMounts: [] + ## Redis® exporter resource requests and limits + ## ref: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/ + ## @param metrics.resourcesPreset Set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). This is ignored if metrics.resources is set (metrics.resources is recommended for production). + ## More information: https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15 + ## + resourcesPreset: "nano" + ## @param metrics.resources Set container requests and limits for different resources like CPU or memory (essential for production workloads) + ## Example: + ## resources: + ## requests: + ## cpu: 2 + ## memory: 512Mi + ## limits: + ## cpu: 3 + ## memory: 1024Mi + ## + resources: {} + ## @param metrics.podLabels Extra labels for Redis® exporter pods + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + ## + podLabels: {} + ## @param metrics.podAnnotations [object] Annotations for Redis® exporter pods + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + ## + podAnnotations: + prometheus.io/scrape: "true" + prometheus.io/port: "9121" + ## Redis® exporter service parameters + ## + service: + ## @param metrics.service.enabled Create Service resource(s) for scraping metrics using PrometheusOperator ServiceMonitor, can be disabled when using a PodMonitor + ## + enabled: true + ## @param metrics.service.type Redis® exporter service type + ## + type: ClusterIP + ## @param metrics.service.ports.http Redis® exporter service port + ## + ports: + http: 9121 + ## @param metrics.service.externalTrafficPolicy Redis® exporter service external traffic policy + ## ref: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip + ## + externalTrafficPolicy: Cluster + ## @param metrics.service.extraPorts Extra ports to expose (normally used with the `sidecar` value) + ## + extraPorts: [] + ## @param metrics.service.loadBalancerIP Redis® exporter service Load Balancer IP + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + loadBalancerIP: "" + ## @param metrics.service.loadBalancerClass exporter service Load Balancer class if service type is `LoadBalancer` (optional, cloud specific) + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-loadbalancer + ## + loadBalancerClass: "" + ## @param metrics.service.loadBalancerSourceRanges Redis® exporter service Load Balancer sources + ## https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service + ## e.g. + ## loadBalancerSourceRanges: + ## - 10.10.10.0/24 + ## + loadBalancerSourceRanges: [] + ## @param metrics.service.annotations Additional custom annotations for Redis® exporter service + ## + annotations: {} + ## @param metrics.service.clusterIP Redis® exporter service Cluster IP + ## + clusterIP: "" + ## Prometheus Service Monitor + ## ref: https://github.com/coreos/prometheus-operator + ## https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#endpoint + ## + serviceMonitor: + ## @param metrics.serviceMonitor.port the service port to scrape metrics from + ## + port: http-metrics + ## @param metrics.serviceMonitor.enabled Create ServiceMonitor resource(s) for scraping metrics using PrometheusOperator + ## + enabled: false + ## @param metrics.serviceMonitor.namespace The namespace in which the ServiceMonitor will be created + ## + namespace: "" + ## @param metrics.serviceMonitor.interval The interval at which metrics should be scraped + ## + interval: 30s + ## @param metrics.serviceMonitor.scrapeTimeout The timeout after which the scrape is ended + ## + scrapeTimeout: "" + ## @param metrics.serviceMonitor.relabelings Metrics RelabelConfigs to apply to samples before scraping. + ## + relabelings: [] + ## @skip metrics.serviceMonitor.relabellings DEPRECATED: Use `metrics.serviceMonitor.relabelings` instead. + ## + relabellings: [] + ## @param metrics.serviceMonitor.metricRelabelings Metrics RelabelConfigs to apply to samples before ingestion. + ## + metricRelabelings: [] + ## @param metrics.serviceMonitor.honorLabels Specify honorLabels parameter to add the scrape endpoint + ## + honorLabels: false + ## @param metrics.serviceMonitor.additionalLabels Additional labels that can be used so ServiceMonitor resource(s) can be discovered by Prometheus + ## + additionalLabels: {} + ## @param metrics.serviceMonitor.podTargetLabels Labels from the Kubernetes pod to be transferred to the created metrics + ## + podTargetLabels: [] + ## @param metrics.serviceMonitor.sampleLimit Limit of how many samples should be scraped from every Pod + ## + sampleLimit: false + ## @param metrics.serviceMonitor.targetLimit Limit of how many targets should be scraped + ## + targetLimit: false + ## @param metrics.serviceMonitor.additionalEndpoints Additional endpoints to scrape (e.g sentinel) + ## + additionalEndpoints: [] + # uncomment in order to scrape sentinel metrics, also to in order distinguish between Sentinel and Redis container metrics + # add metricRelabelings with label like app=redis to main redis pod-monitor port + # - interval: "30s" + # path: "/scrape" + # port: "http-metrics" + # params: + # target: ["localhost:26379"] + # metricRelabelings: + # - targetLabel: "app" + # replacement: "sentinel" + ## Prometheus Pod Monitor + ## ref: https://github.com/coreos/prometheus-operator + ## https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#podmonitor + ## + podMonitor: + ## @param metrics.podMonitor.port the pod port to scrape metrics from + ## + port: metrics + ## @param metrics.podMonitor.enabled Create PodMonitor resource(s) for scraping metrics using PrometheusOperator + ## + enabled: false + ## @param metrics.podMonitor.namespace The namespace in which the PodMonitor will be created + ## + namespace: "" + ## @param metrics.podMonitor.interval The interval at which metrics should be scraped + ## + interval: 30s + ## @param metrics.podMonitor.scrapeTimeout The timeout after which the scrape is ended + ## + scrapeTimeout: "" + ## @param metrics.podMonitor.relabelings Metrics RelabelConfigs to apply to samples before scraping. + ## + relabelings: [] + ## @skip metrics.podMonitor.relabellings DEPRECATED: Use `metrics.podMonitor.relabelings` instead. + ## + relabellings: [] + ## @param metrics.podMonitor.metricRelabelings Metrics RelabelConfigs to apply to samples before ingestion. + ## + metricRelabelings: [] + # - targetLabel: "app" + # replacement: "redis" + ## @param metrics.podMonitor.honorLabels Specify honorLabels parameter to add the scrape endpoint + ## + honorLabels: false + ## @param metrics.podMonitor.additionalLabels Additional labels that can be used so PodMonitor resource(s) can be discovered by Prometheus + ## + additionalLabels: {} + ## @param metrics.podMonitor.podTargetLabels Labels from the Kubernetes pod to be transferred to the created metrics + ## + podTargetLabels: [] + ## @param metrics.podMonitor.sampleLimit Limit of how many samples should be scraped from every Pod + ## + sampleLimit: false + ## @param metrics.podMonitor.targetLimit Limit of how many targets should be scraped + ## + targetLimit: false + ## @param metrics.podMonitor.additionalEndpoints Additional endpoints to scrape (e.g sentinel) + ## + additionalEndpoints: [] + # - interval: "30s" + # path: "/scrape" + # port: "metrics" + # params: + # target: ["localhost:26379"] + # metricRelabelings: + # - targetLabel: "app" + # replacement: "sentinel" + ## Custom PrometheusRule to be defined + ## ref: https://github.com/coreos/prometheus-operator#customresourcedefinitions + ## + prometheusRule: + ## @param metrics.prometheusRule.enabled Create a custom prometheusRule Resource for scraping metrics using PrometheusOperator + ## + enabled: false + ## @param metrics.prometheusRule.namespace The namespace in which the prometheusRule will be created + ## + namespace: "" + ## @param metrics.prometheusRule.additionalLabels Additional labels for the prometheusRule + ## + additionalLabels: {} + ## @param metrics.prometheusRule.rules Custom Prometheus rules + ## e.g: + ## rules: + ## - alert: RedisDown + ## expr: redis_up{service="{{ template "common.names.fullname" . }}-metrics"} == 0 + ## for: 2m + ## labels: + ## severity: error + ## annotations: + ## summary: Redis® instance {{ "{{ $labels.instance }}" }} down + ## description: Redis® instance {{ "{{ $labels.instance }}" }} is down + ## - alert: RedisMemoryHigh + ## expr: > + ## redis_memory_used_bytes{service="{{ template "common.names.fullname" . }}-metrics"} * 100 + ## / + ## redis_memory_max_bytes{service="{{ template "common.names.fullname" . }}-metrics"} + ## > 90 + ## for: 2m + ## labels: + ## severity: error + ## annotations: + ## summary: Redis® instance {{ "{{ $labels.instance }}" }} is using too much memory + ## description: | + ## Redis® instance {{ "{{ $labels.instance }}" }} is using {{ "{{ $value }}" }}% of its available memory. + ## - alert: RedisKeyEviction + ## expr: | + ## increase(redis_evicted_keys_total{service="{{ template "common.names.fullname" . }}-metrics"}[5m]) > 0 + ## for: 1s + ## labels: + ## severity: error + ## annotations: + ## summary: Redis® instance {{ "{{ $labels.instance }}" }} has evicted keys + ## description: | + ## Redis® instance {{ "{{ $labels.instance }}" }} has evicted {{ "{{ $value }}" }} keys in the last 5 minutes. + ## + rules: [] +## @section Init Container Parameters +## + +## 'volumePermissions' init container parameters +## Changes the owner and group of the persistent volume mount point to runAsUser:fsGroup values +## based on the *podSecurityContext/*containerSecurityContext parameters +## +volumePermissions: + ## @param volumePermissions.enabled Enable init container that changes the owner/group of the PV mount point to `runAsUser:fsGroup` + ## + enabled: false + ## OS Shell + Utility image + ## ref: https://hub.docker.com/r/bitnami/os-shell/tags/ + ## @param volumePermissions.image.registry [default: REGISTRY_NAME] OS Shell + Utility image registry + ## @param volumePermissions.image.repository [default: REPOSITORY_NAME/os-shell] OS Shell + Utility image repository + ## @skip volumePermissions.image.tag OS Shell + Utility image tag (immutable tags are recommended) + ## @param volumePermissions.image.digest OS Shell + Utility image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag + ## @param volumePermissions.image.pullPolicy OS Shell + Utility image pull policy + ## @param volumePermissions.image.pullSecrets OS Shell + Utility image pull secrets + ## + image: + registry: docker.io + repository: bitnami/os-shell + tag: 12-debian-12-r34 + digest: "" + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## e.g: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + pullSecrets: [] + ## Init container's resource requests and limits + ## ref: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/ + ## @param volumePermissions.resourcesPreset Set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). This is ignored if volumePermissions.resources is set (volumePermissions.resources is recommended for production). + ## More information: https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15 + ## + resourcesPreset: "nano" + ## @param volumePermissions.resources Set container requests and limits for different resources like CPU or memory (essential for production workloads) + ## Example: + ## resources: + ## requests: + ## cpu: 2 + ## memory: 512Mi + ## limits: + ## cpu: 3 + ## memory: 1024Mi + ## + resources: {} + ## Init container Container Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container + ## @param volumePermissions.containerSecurityContext.seLinuxOptions [object,nullable] Set SELinux options in container + ## @param volumePermissions.containerSecurityContext.runAsUser Set init container's Security Context runAsUser + ## NOTE: when runAsUser is set to special value "auto", init container will try to chown the + ## data folder to auto-determined user&group, using commands: `id -u`:`id -G | cut -d" " -f2` + ## "auto" is especially useful for OpenShift which has scc with dynamic user ids (and 0 is not allowed) + ## + containerSecurityContext: + seLinuxOptions: {} + runAsUser: 0 + + ## @param volumePermissions.extraEnvVars Array with extra environment variables to add to volume permissions init container. + ## e.g: + ## extraEnvVars: + ## - name: FOO + ## value: "bar" + ## + extraEnvVars: [] + +## Kubectl InitContainer +## used by Sentinel to update the isMaster label on the Redis(TM) pods +## +kubectl: + ## Bitnami Kubectl image version + ## ref: https://hub.docker.com/r/bitnami/kubectl/tags/ + ## @param kubectl.image.registry [default: REGISTRY_NAME] Kubectl image registry + ## @param kubectl.image.repository [default: REPOSITORY_NAME/kubectl] Kubectl image repository + ## @skip kubectl.image.tag Kubectl image tag (immutable tags are recommended), by default, using the current version + ## @param kubectl.image.digest Kubectl image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag + ## @param kubectl.image.pullPolicy Kubectl image pull policy + ## @param kubectl.image.pullSecrets Kubectl pull secrets + ## + image: + registry: docker.io + repository: bitnami/kubectl + tag: 1.32.0-debian-12-r0 + digest: "" + ## Specify a imagePullPolicy + ## ref: https://kubernetes.io/docs/concepts/containers/images/#pre-pulled-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## e.g: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + pullSecrets: [] + ## @param kubectl.command kubectl command to execute + ## + command: ["/opt/bitnami/scripts/kubectl-scripts/update-master-label.sh"] + ## Configure Container Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod + ## @param kubectl.containerSecurityContext.enabled Enabled kubectl containers' Security Context + ## @param kubectl.containerSecurityContext.seLinuxOptions [object,nullable] Set SELinux options in container + ## @param kubectl.containerSecurityContext.runAsUser Set kubectl containers' Security Context runAsUser + ## @param kubectl.containerSecurityContext.runAsGroup Set kubectl containers' Security Context runAsGroup + ## @param kubectl.containerSecurityContext.runAsNonRoot Set kubectl containers' Security Context runAsNonRoot + ## @param kubectl.containerSecurityContext.allowPrivilegeEscalation Set kubectl containers' Security Context allowPrivilegeEscalation + ## @param kubectl.containerSecurityContext.readOnlyRootFilesystem Set container's Security Context read-only root filesystem + ## @param kubectl.containerSecurityContext.seccompProfile.type Set kubectl containers' Security Context seccompProfile + ## @param kubectl.containerSecurityContext.capabilities.drop Set kubectl containers' Security Context capabilities to drop + ## + containerSecurityContext: + enabled: true + seLinuxOptions: {} + runAsUser: 1001 + runAsGroup: 1001 + runAsNonRoot: true + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + seccompProfile: + type: RuntimeDefault + capabilities: + drop: ["ALL"] + ## Bitnami Kubectl resource requests and limits + ## ref: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/ + ## @param kubectl.resources.limits The resources limits for the kubectl containers + ## @param kubectl.resources.requests The requested resources for the kubectl containers + ## + resources: + limits: {} + requests: {} + +## init-sysctl container parameters +## used to perform sysctl operation to modify Kernel settings (needed sometimes to avoid warnings) +## +sysctl: + ## @param sysctl.enabled Enable init container to modify Kernel settings + ## + enabled: false + ## OS Shell + Utility image + ## ref: https://hub.docker.com/r/bitnami/os-shell/tags/ + ## @param sysctl.image.registry [default: REGISTRY_NAME] OS Shell + Utility image registry + ## @param sysctl.image.repository [default: REPOSITORY_NAME/os-shell] OS Shell + Utility image repository + ## @skip sysctl.image.tag OS Shell + Utility image tag (immutable tags are recommended) + ## @param sysctl.image.digest OS Shell + Utility image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag + ## @param sysctl.image.pullPolicy OS Shell + Utility image pull policy + ## @param sysctl.image.pullSecrets OS Shell + Utility image pull secrets + ## + image: + registry: docker.io + repository: bitnami/os-shell + tag: 12-debian-12-r34 + digest: "" + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## e.g: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + pullSecrets: [] + ## @param sysctl.command Override default init-sysctl container command (useful when using custom images) + ## + command: [] + ## @param sysctl.mountHostSys Mount the host `/sys` folder to `/host-sys` + ## + mountHostSys: false + ## Init container's resource requests and limits + ## ref: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/ + ## @param sysctl.resourcesPreset Set container resources according to one common preset (allowed values: none, nano, micro, small, medium, large, xlarge, 2xlarge). This is ignored if sysctl.resources is set (sysctl.resources is recommended for production). + ## More information: https://github.com/bitnami/charts/blob/main/bitnami/common/templates/_resources.tpl#L15 + ## + resourcesPreset: "nano" + ## @param sysctl.resources Set container requests and limits for different resources like CPU or memory (essential for production workloads) + ## Example: + ## resources: + ## requests: + ## cpu: 2 + ## memory: 512Mi + ## limits: + ## cpu: 3 + ## memory: 1024Mi + ## + resources: {} +## @section useExternalDNS Parameters +## +## @param useExternalDNS.enabled Enable various syntax that would enable external-dns to work. Note this requires a working installation of `external-dns` to be usable. +## @param useExternalDNS.additionalAnnotations Extra annotations to be utilized when `external-dns` is enabled. +## @param useExternalDNS.annotationKey The annotation key utilized when `external-dns` is enabled. Setting this to `false` will disable annotations. +## @param useExternalDNS.suffix The DNS suffix utilized when `external-dns` is enabled. Note that we prepend the suffix with the full name of the release. +## +useExternalDNS: + enabled: false + suffix: "" + annotationKey: external-dns.alpha.kubernetes.io/ + additionalAnnotations: {} diff --git a/charts/open-webui/charts/tika/.asf.yaml b/charts/open-webui/charts/tika/.asf.yaml new file mode 100644 index 0000000..9cd5f69 --- /dev/null +++ b/charts/open-webui/charts/tika/.asf.yaml @@ -0,0 +1,26 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +github: + description: "A Helm chart to deploy Apache Tika on Kubernetes." + homepage: https://tika.apache.org/ + labels: + - helm + - chart + - tika + +notifications: + commits: commits@tika.apache.org + issues: dev@tika.apache.org + pullrequests: dev@tika.apache.org + jira_options: link label comment diff --git a/charts/open-webui/charts/tika/.github/workflows/lint-test.yaml b/charts/open-webui/charts/tika/.github/workflows/lint-test.yaml new file mode 100644 index 0000000..d3bc11c --- /dev/null +++ b/charts/open-webui/charts/tika/.github/workflows/lint-test.yaml @@ -0,0 +1,64 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Lint and Test Charts + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + types: [opened, synchronize, reopened] + +jobs: + lint-test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Set up Helm + uses: azure/setup-helm@v3 + with: + version: v3.10.0 + + - uses: actions/setup-python@v4 + with: + python-version: '3.9' + check-latest: true + + - name: Set up chart-testing + uses: helm/chart-testing-action@v2.3.1 + + - name: Run chart-testing (list-changed) + id: list-changed + run: | + changed=$(ct list-changed --target-branch ${{ github.event.repository.default_branch }}) + if [[ -n "$changed" ]]; then + echo "::set-output name=changed::true" + fi + + - name: Run chart-testing (lint) + run: ct lint --target-branch ${{ github.event.repository.default_branch }} --charts . + + - name: Create kind cluster + uses: helm/kind-action@v1.4.0 + if: steps.list-changed.outputs.changed == 'true' + + - name: Run chart-testing (install) + run: ct install --target-branch ${{ github.event.repository.default_branch }} --charts . + if: steps.list-changed.outputs.changed == 'true' \ No newline at end of file diff --git a/charts/open-webui/charts/tika/.helmignore b/charts/open-webui/charts/tika/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/charts/open-webui/charts/tika/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/open-webui/charts/tika/Chart.yaml b/charts/open-webui/charts/tika/Chart.yaml new file mode 100644 index 0000000..046e0c9 --- /dev/null +++ b/charts/open-webui/charts/tika/Chart.yaml @@ -0,0 +1,28 @@ +apiVersion: v2 +appVersion: 2.9.0.0 +description: The official Helm chart to deploy Apache Tika on Kubernetes +home: https://tika.apache.org +icon: https://tika.apache.org/tika.png +keywords: +- apache +- tika +- apache-tika +- tika-helm +maintainers: +- email: lewismc@apache.org + name: lewismc + url: https://github.com/lewismc +- name: stijnbrouwers + url: https://github.com/stijnbrouwers +- name: philipsoutham + url: https://github.com/philipsoutham +- name: frascu + url: https://github.com/frascu +- name: euven + url: https://github.com/euven +name: tika +sources: +- https://github.com/apache/tika +- https://github.com/apache/tika-helm +type: application +version: 2.9.0 diff --git a/charts/open-webui/charts/tika/LICENSE.txt b/charts/open-webui/charts/tika/LICENSE.txt new file mode 100644 index 0000000..9badcda --- /dev/null +++ b/charts/open-webui/charts/tika/LICENSE.txt @@ -0,0 +1,5737 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +APACHE NUTCH SUBCOMPONENTS + +lib/commons-httpclient-3.0.1.jar + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + +lib/icu4j-4_0_1.jar + +ICU license - ICU 1.8.1 and later + + COPYRIGHT AND PERMISSION NOTICE + + Copyright (c) 1995-2006 International Business Machines Corporation and + others + + All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, provided that the above copyright notice(s) and this + permission notice appear in all copies of the Software and that both the + above copyright notice(s) and this permission notice appear in supporting + documentation. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY + RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS + NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL + DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + Except as contained in this notice, the name of a copyright holder shall + not be used in advertising or otherwise to promote the sale, use or other + dealings in this Software without prior written authorization of the + copyright holder. + + ---------------------------------------------------------------------- + + All trademarks and registered trademarks mentioned herein are the property + of their respective owners. + +lib/commons-collections-3.2.1.jar + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +lib/commons-logging-1.0.4.jar + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +lib/lucene-misc-2.4.0.jar +src/plugin/summary-lucene/lib/lucene-highlighter-2.4.0.jar +lib/lucene-core-2.4.0.jar +src/plugin/lib-lucene-analyzers/lib/lucene-analyzers-2.4.0.jar + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +Some code in src/java/org/apache/lucene/util/UnicodeUtil.java was +derived from unicode conversion examples available at +http://www.unicode.org/Public/PROGRAMS/CVTUTF. Here is the copyright +from those sources: + +/* + * Copyright 2001-2004 Unicode, Inc. + * + * Disclaimer + * + * This source code is provided as is by Unicode, Inc. No claims are + * made as to fitness for any particular purpose. No warranties of any + * kind are expressed or implied. The recipient agrees to determine + * applicability of information provided. If this file has been + * purchased on magnetic or optical media from Unicode, Inc., the + * sole remedy for any claim will be exchange of defective media + * within 90 days of receipt. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + + +Some code in src/java/org/apache/lucene/util/ArrayUtil.java was +derived from Python 2.4.2 sources available at +http://www.python.org. Full license is here: + + http://www.python.org/download/releases/2.4.2/license/ + +lib/jetty-ext/commons-el.jar + +/* + * $Header: /home/cvs/jakarta-commons/el/LICENSE.txt,v 1.1.1.1 2003/02/04 00:22:24 luehe Exp $ + * $Revision: 1.1.1.1 $ + * $Date: 2003/02/04 00:22:24 $ + * + * ==================================================================== + * + * The Apache Software License, Version 1.1 + * + * Copyright (c) 1999-2002 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Commons", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ + +lib/jetty-ext/ant.jar + +/* + * Apache License + * Version 2.0, January 2004 + * http://www.apache.org/licenses/ + * + * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + * + * 1. Definitions. + * + * "License" shall mean the terms and conditions for use, reproduction, + * and distribution as defined by Sections 1 through 9 of this document. + * + * "Licensor" shall mean the copyright owner or entity authorized by + * the copyright owner that is granting the License. + * + * "Legal Entity" shall mean the union of the acting entity and all + * other entities that control, are controlled by, or are under common + * control with that entity. For the purposes of this definition, + * "control" means (i) the power, direct or indirect, to cause the + * direction or management of such entity, whether by contract or + * otherwise, or (ii) ownership of fifty percent (50%) or more of the + * outstanding shares, or (iii) beneficial ownership of such entity. + * + * "You" (or "Your") shall mean an individual or Legal Entity + * exercising permissions granted by this License. + * + * "Source" form shall mean the preferred form for making modifications, + * including but not limited to software source code, documentation + * source, and configuration files. + * + * "Object" form shall mean any form resulting from mechanical + * transformation or translation of a Source form, including but + * not limited to compiled object code, generated documentation, + * and conversions to other media types. + * + * "Work" shall mean the work of authorship, whether in Source or + * Object form, made available under the License, as indicated by a + * copyright notice that is included in or attached to the work + * (an example is provided in the Appendix below). + * + * "Derivative Works" shall mean any work, whether in Source or Object + * form, that is based on (or derived from) the Work and for which the + * editorial revisions, annotations, elaborations, or other modifications + * represent, as a whole, an original work of authorship. For the purposes + * of this License, Derivative Works shall not include works that remain + * separable from, or merely link (or bind by name) to the interfaces of, + * the Work and Derivative Works thereof. + * + * "Contribution" shall mean any work of authorship, including + * the original version of the Work and any modifications or additions + * to that Work or Derivative Works thereof, that is intentionally + * submitted to Licensor for inclusion in the Work by the copyright owner + * or by an individual or Legal Entity authorized to submit on behalf of + * the copyright owner. For the purposes of this definition, "submitted" + * means any form of electronic, verbal, or written communication sent + * to the Licensor or its representatives, including but not limited to + * communication on electronic mailing lists, source code control systems, + * and issue tracking systems that are managed by, or on behalf of, the + * Licensor for the purpose of discussing and improving the Work, but + * excluding communication that is conspicuously marked or otherwise + * designated in writing by the copyright owner as "Not a Contribution." + * + * "Contributor" shall mean Licensor and any individual or Legal Entity + * on behalf of whom a Contribution has been received by Licensor and + * subsequently incorporated within the Work. + * + * 2. Grant of Copyright License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * copyright license to reproduce, prepare Derivative Works of, + * publicly display, publicly perform, sublicense, and distribute the + * Work and such Derivative Works in Source or Object form. + * + * 3. Grant of Patent License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * (except as stated in this section) patent license to make, have made, + * use, offer to sell, sell, import, and otherwise transfer the Work, + * where such license applies only to those patent claims licensable + * by such Contributor that are necessarily infringed by their + * Contribution(s) alone or by combination of their Contribution(s) + * with the Work to which such Contribution(s) was submitted. If You + * institute patent litigation against any entity (including a + * cross-claim or counterclaim in a lawsuit) alleging that the Work + * or a Contribution incorporated within the Work constitutes direct + * or contributory patent infringement, then any patent licenses + * granted to You under this License for that Work shall terminate + * as of the date such litigation is filed. + * + * 4. Redistribution. You may reproduce and distribute copies of the + * Work or Derivative Works thereof in any medium, with or without + * modifications, and in Source or Object form, provided that You + * meet the following conditions: + * + * (a) You must give any other recipients of the Work or + * Derivative Works a copy of this License; and + * + * (b) You must cause any modified files to carry prominent notices + * stating that You changed the files; and + * + * (c) You must retain, in the Source form of any Derivative Works + * that You distribute, all copyright, patent, trademark, and + * attribution notices from the Source form of the Work, + * excluding those notices that do not pertain to any part of + * the Derivative Works; and + * + * (d) If the Work includes a "NOTICE" text file as part of its + * distribution, then any Derivative Works that You distribute must + * include a readable copy of the attribution notices contained + * within such NOTICE file, excluding those notices that do not + * pertain to any part of the Derivative Works, in at least one + * of the following places: within a NOTICE text file distributed + * as part of the Derivative Works; within the Source form or + * documentation, if provided along with the Derivative Works; or, + * within a display generated by the Derivative Works, if and + * wherever such third-party notices normally appear. The contents + * of the NOTICE file are for informational purposes only and + * do not modify the License. You may add Your own attribution + * notices within Derivative Works that You distribute, alongside + * or as an addendum to the NOTICE text from the Work, provided + * that such additional attribution notices cannot be construed + * as modifying the License. + * + * You may add Your own copyright statement to Your modifications and + * may provide additional or different license terms and conditions + * for use, reproduction, or distribution of Your modifications, or + * for any such Derivative Works as a whole, provided Your use, + * reproduction, and distribution of the Work otherwise complies with + * the conditions stated in this License. + * + * 5. Submission of Contributions. Unless You explicitly state otherwise, + * any Contribution intentionally submitted for inclusion in the Work + * by You to the Licensor shall be under the terms and conditions of + * this License, without any additional terms or conditions. + * Notwithstanding the above, nothing herein shall supersede or modify + * the terms of any separate license agreement you may have executed + * with Licensor regarding such Contributions. + * + * 6. Trademarks. This License does not grant permission to use the trade + * names, trademarks, service marks, or product names of the Licensor, + * except as required for reasonable and customary use in describing the + * origin of the Work and reproducing the content of the NOTICE file. + * + * 7. Disclaimer of Warranty. Unless required by applicable law or + * agreed to in writing, Licensor provides the Work (and each + * Contributor provides its Contributions) on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied, including, without limitation, any warranties or conditions + * of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + * PARTICULAR PURPOSE. You are solely responsible for determining the + * appropriateness of using or redistributing the Work and assume any + * risks associated with Your exercise of permissions under this License. + * + * 8. Limitation of Liability. In no event and under no legal theory, + * whether in tort (including negligence), contract, or otherwise, + * unless required by applicable law (such as deliberate and grossly + * negligent acts) or agreed to in writing, shall any Contributor be + * liable to You for damages, including any direct, indirect, special, + * incidental, or consequential damages of any character arising as a + * result of this License or out of the use or inability to use the + * Work (including but not limited to damages for loss of goodwill, + * work stoppage, computer failure or malfunction, or any and all + * other commercial damages or losses), even if such Contributor + * has been advised of the possibility of such damages. + * + * 9. Accepting Warranty or Additional Liability. While redistributing + * the Work or Derivative Works thereof, You may choose to offer, + * and charge a fee for, acceptance of support, warranty, indemnity, + * or other liability obligations and/or rights consistent with this + * License. However, in accepting such obligations, You may act only + * on Your own behalf and on Your sole responsibility, not on behalf + * of any other Contributor, and only if You agree to indemnify, + * defend, and hold each Contributor harmless for any liability + * incurred by, or claims asserted against, such Contributor by reason + * of your accepting any such warranty or additional liability. + * + * END OF TERMS AND CONDITIONS + * + * APPENDIX: How to apply the Apache License to your work. + * + * To apply the Apache License to your work, attach the following + * boilerplate notice, with the fields enclosed by brackets "[]" + * replaced with your own identifying information. (Don't include + * the brackets!) The text should be enclosed in the appropriate + * comment syntax for the file format. We also recommend that a + * file or class name and description of purpose be included on the + * same "printed page" as the copyright notice for easier + * identification within third-party archives. + * + * Copyright [yyyy] [name of copyright owner] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +lib/jetty-ext/jsp-api.jar +ASF + +lib/jetty-ext/jasper-runtime.jar +ASF + +lib/jetty-ext/jasper-compiler.jar +ASF + +lib/hadoop-0.19.1-core.jar + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +APACHE HADOOP SUBCOMPONENTS: + +The Apache Hadoop project contains subcomponents with separate copyright +notices and license terms. Your use of the source code for the these +subcomponents is subject to the terms and conditions of the following +licenses. + +For the org.apache.hadoop.util.bloom.* classes: + +/** + * + * Copyright (c) 2005, European Commission project OneLab under contract + * 034819 (http://www.one-lab.org) + * All rights reserved. + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of the University Catholique de Louvain - UCL + * nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +lib/apache-solr-common-1.3.0.jar +lib/apache-solr-solrj-1.3.0.jar + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +========================================================================== +Portions of Jetty 6 are bundled in the Solr example server. +Jetty 6 includes a binary javax.servlet package licensed under the +Common Development and Distribution License. +-------------------------------------------------------------------------- +COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 + +1. Definitions. + +1.1. Contributor means each individual or entity that creates or contributes to +the creation of Modifications. + +1.2. Contributor Version means the combination of the Original Software, prior +Modifications used by a Contributor (if any), and the Modifications made by +that particular Contributor. + +1.3. Covered Software means (a) the Original Software, or (b) Modifications, or +(c) the combination of files containing Original Software with files containing +Modifications, in each case including portions thereof. + +1.4. Executable means the Covered Software in any form other than Source Code. + +1.5. Initial Developer means the individual or entity that first makes Original +Software available under this License. + +1.6. Larger Work means a work which combines Covered Software or portions +thereof with code not governed by the terms of this License. + +1.7. License means this document. + +1.8. Licensable means having the right to grant, to the maximum extent +possible, whether at the time of the initial grant or subsequently acquired, +any and all of the rights conveyed herein. + +1.9. Modifications means the Source Code and Executable form of any of the +following: + +A. Any file that results from an addition to, deletion from or modification of +the contents of a file containing Original Software or previous Modifications; + +B. Any new file that contains any part of the Original Software or previous +Modification; or + +C. Any new file that is contributed or otherwise made available under the terms +of this License. + +1.10. Original Software means the Source Code and Executable form of computer +software code that is originally released under this License. + +1.11. Patent Claims means any patent claim(s), now owned or hereafter acquired, +including without limitation, method, process, and apparatus claims, in any +patent Licensable by grantor. + +1.12. Source Code means (a) the common form of computer software code in which +modifications are made and (b) associated documentation included in or with +such code. + +1.13. You (or Your) means an individual or a legal entity exercising rights +under, and complying with all of the terms of, this License. For legal +entities, You includes any entity which controls, is controlled by, or is under +common control with You. For purposes of this definition, control means (a)áthe +power, direct or indirect, to cause the direction or management of such entity, +whether by contract or otherwise, or (b)áownership of more than fifty percent +(50%) of the outstanding shares or beneficial ownership of such entity. + +2. License Grants. + +2.1. The Initial Developer Grant. Conditioned upon Your compliance with +Section 3.1 below and subject to third party intellectual property claims, the +Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive +license: (a) under intellectual property rights (other than patent or +trademark) Licensable by Initial Developer, to use, reproduce, modify, display, +perform, sublicense and distribute the Original Software (or portions thereof), +with or without Modifications, and/or as part of a Larger Work; and (b) under +Patent Claims infringed by the making, using or selling of Original Software, +to make, have made, use, practice, sell, and offer for sale, and/or otherwise +dispose of the Original Software (or portions thereof). (c) The licenses +granted in Sectionsá2.1(a) and (b) are effective on the date Initial Developer +first distributes or otherwise makes the Original Software available to a third +party under the terms of this License. (d) Notwithstanding Sectioná2.1(b) +above, no patent license is granted: (1)áfor code that You delete from the +Original Software, or (2)áfor infringements caused by: (i)áthe modification of +the Original Software, or (ii)áthe combination of the Original Software with +other software or devices. + +2.2. Contributor Grant. Conditioned upon Your compliance with Section 3.1 +below and subject to third party intellectual property claims, each Contributor +hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under +intellectual property rights (other than patent or trademark) Licensable by +Contributor to use, reproduce, modify, display, perform, sublicense and +distribute the Modifications created by such Contributor (or portions thereof), +either on an unmodified basis, with other Modifications, as Covered Software +and/or as part of a Larger Work; and (b) under Patent Claims infringed by the +making, using, or selling of Modifications made by that Contributor either +alone and/or in combination with its Contributor Version (or portions of such +combination), to make, use, sell, offer for sale, have made, and/or otherwise +dispose of: (1)áModifications made by that Contributor (or portions thereof); +and (2)áthe combination of Modifications made by that Contributor with its +Contributor Version (or portions of such combination). (c) The licenses +granted in Sectionsá2.2(a) and 2.2(b) are effective on the date Contributor +first distributes or otherwise makes the Modifications available to a third +party. (d) Notwithstanding Sectioná2.2(b) above, no patent license is granted: +(1)áfor any code that Contributor has deleted from the Contributor Version; +(2)áfor infringements caused by: (i)áthird party modifications of Contributor +Version, or (ii)áthe combination of Modifications made by that Contributor with +other software (except as part of the Contributor Version) or other devices; or +(3)áunder Patent Claims infringed by Covered Software in the absence of +Modifications made by that Contributor. + +3. Distribution Obligations. + +3.1. Availability of Source Code. + +Any Covered Software that You distribute or otherwise make available in +Executable form must also be made available in Source Code form and that Source +Code form must be distributed only under the terms of this License. You must +include a copy of this License with every copy of the Source Code form of the +Covered Software You distribute or otherwise make available. You must inform +recipients of any such Covered Software in Executable form as to how they can +obtain such Covered Software in Source Code form in a reasonable manner on or +through a medium customarily used for software exchange. + +3.2. Modifications. + +The Modifications that You create or to which You contribute are governed by +the terms of this License. You represent that You believe Your Modifications +are Your original creation(s) and/or You have sufficient rights to grant the +rights conveyed by this License. + +3.3. Required Notices. You must include a notice in each of Your Modifications +that identifies You as the Contributor of the Modification. You may not remove +or alter any copyright, patent or trademark notices contained within the +Covered Software, or any notices of licensing or any descriptive text giving +attribution to any Contributor or the Initial Developer. + +3.4. Application of Additional Terms. You may not offer or impose any terms on +any Covered Software in Source Code form that alters or restricts the +applicable version of this License or the recipients rights hereunder. You may +choose to offer, and to charge a fee for, warranty, support, indemnity or +liability obligations to one or more recipients of Covered Software. However, +you may do so only on Your own behalf, and not on behalf of the Initial +Developer or any Contributor. You must make it absolutely clear that any such +warranty, support, indemnity or liability obligation is offered by You alone, +and You hereby agree to indemnify the Initial Developer and every Contributor +for any liability incurred by the Initial Developer or such Contributor as a +result of warranty, support, indemnity or liability terms You offer. + +3.5. Distribution of Executable Versions. You may distribute the Executable +form of the Covered Software under the terms of this License or under the terms +of a license of Your choice, which may contain terms different from this +License, provided that You are in compliance with the terms of this License and +that the license for the Executable form does not attempt to limit or alter the +recipients rights in the Source Code form from the rights set forth in this +License. If You distribute the Covered Software in Executable form under a +different license, You must make it absolutely clear that any terms which +differ from this License are offered by You alone, not by the Initial Developer +or Contributor. You hereby agree to indemnify the Initial Developer and every +Contributor for any liability incurred by the Initial Developer or such +Contributor as a result of any such terms You offer. + +3.6. Larger Works. You may create a Larger Work by combining Covered Software +with other code not governed by the terms of this License and distribute the +Larger Work as a single product. In such a case, You must make sure the +requirements of this License are fulfilled for the Covered Software. + +4. Versions of the License. + +4.1. New Versions. Sun Microsystems, Inc. is the initial license steward and +may publish revised and/or new versions of this License from time to time. Each +version will be given a distinguishing version number. Except as provided in +Section 4.3, no one other than the license steward has the right to modify this +License. + +4.2. Effect of New Versions. + +You may always continue to use, distribute or otherwise make the Covered +Software available under the terms of the version of the License under which +You originally received the Covered Software. If the Initial Developer includes +a notice in the Original Software prohibiting it from being distributed or +otherwise made available under any subsequent version of the License, You must +distribute and make the Covered Software available under the terms of the +version of the License under which You originally received the Covered +Software. Otherwise, You may also choose to use, distribute or otherwise make +the Covered Software available under the terms of any subsequent version of the +License published by the license steward. 4.3. Modified Versions. + +When You are an Initial Developer and You want to create a new license for Your +Original Software, You may create and use a modified version of this License if +You: (a)árename the license and remove any references to the name of the +license steward (except to note that the license differs from this License); +and (b)áotherwise make it clear that the license contains terms which differ +from this License. + +5. DISCLAIMER OF WARRANTY. + +COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN AS IS BASIS, WITHOUT +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT +LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF DEFECTS, +MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK +AS TO THE QUALITY AND PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD +ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL +DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, +REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART +OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT +UNDER THIS DISCLAIMER. + +6. TERMINATION. + +6.1. This License and the rights granted hereunder will terminate automatically +if You fail to comply with terms herein and fail to cure such breach within 30 +days of becoming aware of the breach. Provisions which, by their nature, must +remain in effect beyond the termination of this License shall survive. + +6.2. If You assert a patent infringement claim (excluding declaratory judgment +actions) against Initial Developer or a Contributor (the Initial Developer or +Contributor against whom You assert such claim is referred to as Participant) +alleging that the Participant Software (meaning the Contributor Version where +the Participant is a Contributor or the Original Software where the Participant +is the Initial Developer) directly or indirectly infringes any patent, then any +and all rights granted directly or indirectly to You by such Participant, the +Initial Developer (if the Initial Developer is not the Participant) and all +Contributors under Sectionsá2.1 and/or 2.2 of this License shall, upon 60 days +notice from Participant terminate prospectively and automatically at the +expiration of such 60 day notice period, unless if within such 60 day period +You withdraw Your claim with respect to the Participant Software against such +Participant either unilaterally or pursuant to a written agreement with +Participant. + +6.3. In the event of termination under Sectionsá6.1 or 6.2 above, all end user +licenses that have been validly granted by You or any distributor hereunder +prior to termination (excluding licenses granted to You by any distributor) +shall survive termination. + +7. LIMITATION OF LIABILITY. + +UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING +NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY +OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED SOFTWARE, OR ANY SUPPLIER OF +ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, +INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT +LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER +FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN +IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS +LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL +INJURY RESULTING FROM SUCH PARTYS NEGLIGENCE TO THE EXTENT APPLICABLE LAW +PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR +LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND +LIMITATION MAY NOT APPLY TO YOU. + +8. U.S. GOVERNMENT END USERS. + +The Covered Software is a commercial item, as that term is defined in +48áC.F.R.á2.101 (Oct. 1995), consisting of commercial computer software (as +that term is defined at 48 C.F.R. á252.227-7014(a)(1)) and commercial computer +software documentation as such terms are used in 48áC.F.R.á12.212 (Sept. 1995). +Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 +(June 1995), all U.S. Government End Users acquire Covered Software with only +those rights set forth herein. This U.S. Government Rights clause is in lieu +of, and supersedes, any other FAR, DFAR, or other clause or provision that +addresses Government rights in computer software under this License. + +9. MISCELLANEOUS. + +This License represents the complete agreement concerning subject matter +hereof. If any provision of this License is held to be unenforceable, such +provision shall be reformed only to the extent necessary to make it +enforceable. This License shall be governed by the law of the jurisdiction +specified in a notice contained within the Original Software (except to the +extent applicable law, if any, provides otherwise), excluding such +jurisdictions conflict-of-law provisions. Any litigation relating to this +License shall be subject to the jurisdiction of the courts located in the +jurisdiction and venue specified in a notice contained within the Original +Software, with the losing party responsible for costs, including, without +limitation, court costs and reasonable attorneys fees and expenses. The +application of the United Nations Convention on Contracts for the International +Sale of Goods is expressly excluded. Any law or regulation which provides that +the language of a contract shall be construed against the drafter shall not +apply to this License. You agree that You alone are responsible for compliance +with the United States export administration regulations (and the export +control laws and regulation of any other countries) when You use, distribute or +otherwise make available any Covered Software. + +10. RESPONSIBILITY FOR CLAIMS. + +As between Initial Developer and the Contributors, each party is responsible +for claims and damages arising, directly or indirectly, out of its utilization +of rights under this License and You agree to work with Initial Developer and +Contributors to distribute such responsibility on an equitable basis. Nothing +herein is intended or shall be deemed to constitute any admission of liability. + +NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE +(CDDL) The GlassFish code released under the CDDL shall be governed by the laws +of the State of California (excluding conflict-of-law provisions). Any +litigation relating to this License shall be subject to the jurisdiction of the +Federal Courts of the Northern District of California and the state courts of +the State of California, with venue lying in Santa Clara County, California. + + +========================================================================== +The following license applies to parts of the lucene-snowball jar +that are generated from the snowball sources at http://snowball.tartarus.org/ +-------------------------------------------------------------------------- +The BSD License + +Copyright (c) 2001, Dr Martin Porter, Copyright (c) 2002, Richard Boulton +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of the nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +========================================================================== +The following license applies to easymock.jar +-------------------------------------------------------------------------- +EasyMock 2 License (MIT License) +Copyright (c) 2001-2007 OFFIS, Tammo Freese. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +========================================================================== +The following license applies to the JQuery JavaScript library +-------------------------------------------------------------------------- +Copyright (c) 2008 John Resig, http://jquery.com/ + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +========================================================================== +The following license applies to stax-utils.jar +-------------------------------------------------------------------------- +Copyright (c) 2004, Christian Niles, unit12.net +Copyright (c) 2004, Sun Microsystems, Inc. +Copyright (c) 2006, John Kristian +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of the listed copyright holders nor the names + of its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +========================================================================== +The following license applies to JUnit +-------------------------------------------------------------------------- +Common Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + + a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and + b) in the case of each subsequent Contributor: + + i) changes to the Program, and + + ii) additions to the Program; + + where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. + +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. + +2. GRANT OF RIGHTS + + a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. + + b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. + + c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. + + d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. + +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + + a) it complies with the terms and conditions of this Agreement; and + + b) its license agreement: + + i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; + + ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; + + iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and + + iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. + +When the Program is made available in source code form: + + a) it must be made available under this Agreement; and + + b) a copy of this Agreement must be included with each copy of the Program. + +Contributors may not remove or alter any copyright notices contained within the Program. + +Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed. In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. IBM is the initial Agreement Steward. IBM may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. + + +lib/xerces-2_6_2-apis.jar + +xml-commons/java/external/LICENSE.sax.txt $Id: LICENSE.sax.txt,v 1.1 2002/01/31 23:26:48 curcuru Exp $ + + +This license came from: http://www.megginson.com/SAX/copying.html + However please note future versions of SAX may be covered + under http://saxproject.org/?selected=pd + + +This page is now out of date -- see the new SAX site at +http://www.saxproject.org/ for more up-to-date +releases and other information. Please change your bookmarks. + + +SAX2 is Free! + +I hereby abandon any property rights to SAX 2.0 (the Simple API for +XML), and release all of the SAX 2.0 source code, compiled code, and +documentation contained in this distribution into the Public Domain. +SAX comes with NO WARRANTY or guarantee of fitness for any +purpose. + +David Megginson, david@megginson.com +2000-05-05 + + +xml-commons/java/external/LICENSE.dom-software.txt $Id: LICENSE.dom-software.txt,v 1.1 2002/01/31 23:13:42 curcuru Exp $ + + +This license came from: http://www.w3.org/Consortium/Legal/copyright-software-19980720 + + +W3C® SOFTWARE NOTICE AND LICENSE +Copyright © 1994-2001 World +Wide Web Consortium, World +Wide Web Consortium, (Massachusetts Institute of +Technology, Institut National de +Recherche en Informatique et en Automatique, Keio University). All Rights Reserved. +http://www.w3.org/Consortium/Legal/ + +This W3C work (including software, documents, or other related +items) is being provided by the copyright holders under the +following license. By obtaining, using and/or copying this work, +you (the licensee) agree that you have read, understood, and will +comply with the following terms and conditions: +Permission to use, copy, modify, and distribute this software +and its documentation, with or without modification,  for any +purpose and without fee or royalty is hereby granted, provided that +you include the following on ALL copies of the software and +documentation or portions thereof, including modifications, that +you make: + +The full text of this NOTICE in a location viewable to users of +the redistributed or derivative work. + +Any pre-existing intellectual property disclaimers, notices, or +terms and conditions. If none exist, a short notice of the +following form (hypertext is preferred, text is permitted) should +be used within the body of any redistributed or derivative code: +"Copyright © [$date-of-software] World Wide Web Consortium, (Massachusetts Institute of +Technology, Institut National de +Recherche en Informatique et en Automatique, Keio University). All Rights Reserved. +http://www.w3.org/Consortium/Legal/" + +Notice of any changes or modifications to the W3C files, +including the date changes were made. (We recommend you provide +URIs to the location from which the code is derived.) + +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND +COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF +MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE +USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD +PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. +COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, +SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE +SOFTWARE OR DOCUMENTATION. + +The name and trademarks of copyright holders may NOT be used in +advertising or publicity pertaining to the software without +specific, written prior permission. Title to copyright in this +software and any associated documentation will at all times remain +with copyright holders. +____________________________________ +This formulation of W3C's notice and license became active on +August 14 1998 so as to improve compatibility with GPL. This +version ensures that W3C software licensing terms are no more +restrictive than GPL and consequently W3C software may be +distributed in GPL packages. See the older formulation for the +policy prior to this date. Please see our Copyright FAQ for common +questions about using materials from +our site, including specific terms and conditions for packages like +libwww, Amaya, and Jigsaw. +Other questions about this notice can be +directed to site-policy@w3.org. + +webmaster +(last updated $Date: 2002/01/31 23:13:42 $) + +lib/commons-cli-2.0-SNAPSHOT.jar + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +lib/commons-codec-1.3.jar + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +lib/xerces-2_6_2.jar +ASF + +lib/jetty-5.1.4.jar + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +lib/commons-lang-2.1.jar + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +lib/commons-beanutils-1.8.0.jar + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +lib/commons-logging-api-1.0.4.jar + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +lib/servlet-api.jar +ASF + +lib/jets3t-0.6.1.jar + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +lib/tika-0.1-incubating.jar + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +lib/junit-3.8.1.jar + +Common Public License Version 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + + a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and + + b) in the case of each subsequent Contributor: + + i) changes to the Program, and + + ii) additions to the Program; + + where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. + +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. + +2. GRANT OF RIGHTS + + a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. + + b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. + + c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. + + d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. + +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + + a) it complies with the terms and conditions of this Agreement; and + + b) its license agreement: + + i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; + + ii) effectively excludes on behalf of all Cntributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; + + iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and + + iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. + +When the Program is made available in source code form: + + a) it must be made available under this Agreement; and + + b) a copy of this Agreement must be included with each copy of the Program. + +Contributors may not remove or alter any copyright notices contained within the Program. + +Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PR LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed. In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. IBM is the initial Agreement Steward. IBM may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. +OFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + +If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed. In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. + +All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. IBM is the initial Agreement Steward. IBM may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. + +lib/taglibs-i18n.jar +ASF + +lib/log4j-1.2.15.jar +ASF + +src/plugin/feed/lib/rome-0.9.jar +Copyright 2004 Sun Microsystems, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +src/plugin/lib-jakarta-poi/lib/poi-scratchpad-3.5-beta4-20081128.jar + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + + +Office Open XML (OOXML) xsds: +----------------------------- + +These were downloaded as part of the Office Open XML ECMA Specification +from + +These are included within the Apache POI distribution, and are available +under compatible licensing terms. + +Copyright - ECMA International, "made available without restriction" + http://www.ecma-international.org/memento/Ecmabylaws.htm - section 9.4 +Patent License - Microsoft Open Specification Promise (OSP) + http://www.microsoft.com/interop/osp/ + + +src/plugin/lib-jakarta-poi/lib/poi-3.5-beta4-20081128.jar + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + + +Office Open XML (OOXML) xsds: +----------------------------- + +These were downloaded as part of the Office Open XML ECMA Specification +from + +These are included within the Apache POI distribution, and are available +under compatible licensing terms. + +Copyright - ECMA International, "made available without restriction" + http://www.ecma-international.org/memento/Ecmabylaws.htm - section 9.4 +Patent License - Microsoft Open Specification Promise (OSP) + http://www.microsoft.com/interop/osp/ + +src/plugin/urlfilter-automaton/lib/automaton.jar + +dk.brics.automaton +------------------ + +Copyright (C) 2001-2004 Anders Moeller + +This source code in this package may be used under the terms of the +BSD license. Please read the file 'COPYING' for details. + +This package contains a full DFA/NFA implementation with Unicode +alphabet and support for all standard regular expression operations. + +For more information, go to the package home page at +http://www.brics.dk/~amoeller/automaton/ + + +Anders Moeller +amoeller@brics.dk + +src/plugin/lib-nekohtml/lib/nekohtml-0.9.4.jar + +The CyberNeko Software License, Version 1.0 + + +(C) Copyright 2002,2003, Andy Clark. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +3. The end-user documentation included with the redistribution, + if any, must include the following acknowledgment: + "This product includes software developed by Andy Clark." + Alternately, this acknowledgment may appear in the software itself, + if and wherever such third-party acknowledgments normally appear. + +4. The names "CyberNeko" and "NekoHTML" must not be used to endorse + or promote products derived from this software without prior + written permission. For written permission, please contact + andy@cyberneko.net. + +5. Products derived from this software may not be called "CyberNeko", + nor may "CyberNeko" appear in their name, without prior written + permission of the author. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +==================================================================== + +This license is based on the Apache Software License, version 1.1. + + +src/plugin/clustering-carrot2/lib/violinstrings-1.0.2.jar + +Copyright (c) Michael Schmeling 1998, 2000 - All Rights Reserved + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, provided that the above copyright notice(s) and this +permission notice appear in all copies of the Software and that both the +above copyright notice(s) and this permission notice appear in supporting +documentation. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in this Software without prior written authorization of the +copyright holder. + +src/plugin/clustering-carrot2/lib/carrot2-util-common.jar +src/plugin/clustering-carrot2/lib/carrot2-filter-lingo.jar +src/plugin/clustering-carrot2/lib/carrot2-snowball-stemmers.jar +src/plugin/clustering-carrot2/lib/carrot2-util-tokenizer.jar +src/plugin/clustering-carrot2/lib/carrot2-local-core.jar + + +Carrot2 Project + +Copyright (C) 2002-2006, Dawid Weiss, Stanis�aw Osi�ski. +Portions (C) Contributors listed in "carrot2.CONTRIBUTORS" file. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +- Neither the name of the Poznan University of Technology, Poznan, Poland nor + the names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +- We request that you include in the end-user documentation provided with the + redistribution and/or in the software itself an acknowledgement equivalent to + the following: "This product includes software developed by the Carrot2 + Project." + +- No algorithms or technical solutions in the project may be patented or claimed + proprietary. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +src/plugin/clustering-carrot2/lib/commons-collections-3.2.jar +ASF + +src/plugin/clustering-carrot2/lib/Jama-1.0.2.jar + +Copyright Notice This software is a cooperative product of The MathWorks and the +National Institute of Standards and Technology (NIST) which has been released to +the public domain. Neither The MathWorks nor NIST assumes any responsibility +whatsoever for its use by other parties, and makes no guarantees, expressed or +implied, about its quality, reliability, or any other characteristic. + +src/plugin/clustering-carrot2/lib/commons-pool-1.3.jar +ASF + +src/plugin/protocol-ftp/lib/commons-net-1.2.0-dev.jar +ASF + +src/plugin/ontology/lib/jena-2.1.jar + +/* + * (c) Copyright 2000, 2001, 2002, 2003, 2004 Hewlett-Packard Development Company, LP + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +This product includes software developed by the +Apache Software Foundation (http://www.apache.org/). + +src/plugin/parse-swf/lib/javaswf.jar + + + Copyright (c) 2001-2005, David N. Main, All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the + following conditions are met: + + 1. Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + + 2. Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + 3. The name of the author may not be used to endorse or + promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +src/plugin/response-json/lib/json-lib-2.2.2-jdk15.jar + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +src/plugin/response-json/lib/ezmorph-1.0.6.jar + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +src/plugin/parse-html/lib/tagsoup-1.2.jar + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +src/plugin/parse-pdf/lib/PDFBox-0.7.4-dev.jar + +Copyright (c) 2003-2005, www.pdfbox.org +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +3. Neither the name of pdfbox; nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +src/plugin/parse-pdf/lib/JempBox-0.2.0.jar + +Copyright (c) 2006-2007, www.jempbox.org +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +3. Neither the name of pdfbox; nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +src/plugin/parse-pdf/lib/FontBox-0.2.0-dev.jar + +Copyright (c) 2003-2005, www.fontbox.org +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +3. Neither the name of fontbox; nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +src/plugin/parse-pdf/lib/bcprov-jdk14-132.jar + +Copyright (c) 2000 - 2008 The Legion Of The Bouncy Castle (http://www.bouncycastle.org) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +src/plugin/lib-xml/lib/jdom.jar + +/*-- + + $Id: LICENSE.txt,v 1.11 2004/02/06 09:32:57 jhunter Exp $ + + Copyright (C) 2000-2004 Jason Hunter & Brett McLaughlin. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the disclaimer that follows + these conditions in the documentation and/or other materials + provided with the distribution. + + 3. The name "JDOM" must not be used to endorse or promote products + derived from this software without prior written permission. For + written permission, please contact . + + 4. Products derived from this software may not be called "JDOM", nor + may "JDOM" appear in their name, without prior written permission + from the JDOM Project Management . + + In addition, we request (but do not require) that you include in the + end-user documentation provided with the redistribution and/or in the + software itself an acknowledgement equivalent to the following: + "This product includes software developed by the + JDOM Project (http://www.jdom.org/)." + Alternatively, the acknowledgment may be graphical using the logos + available at http://www.jdom.org/images/logos. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + This software consists of voluntary contributions made by many + individuals on behalf of the JDOM Project and was originally + created by Jason Hunter and + Brett McLaughlin . For more information + on the JDOM Project, please see . + + */ + +src/plugin/lib-xml/lib/jaxen-jdom.jar +src/plugin/lib-xml/lib/jaxen-core.jar + +/* + $Id: LICENSE.txt 1128 2006-02-05 21:49:04Z elharo $ + + Copyright 2003-2006 The Werken Company. All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the Jaxen Project nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + */ + + +src/plugin/lib-xml/lib/saxpath.jar + +/*-- + + $Id: LICENSE,v 1.1 2002/04/26 17:43:56 jstrachan Exp $ + + Copyright (C) 2000-2002 werken digital. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions, and the disclaimer that follows + these conditions in the documentation and/or other materials + provided with the distribution. + + 3. The name "SAXPath" must not be used to endorse or promote products + derived from this software without prior written permission. For + written permission, please contact license@saxpath.org. + + 4. Products derived from this software may not be called "SAXPath", nor + may "SAXPath" appear in their name, without prior written permission + from the SAXPath Project Management (pm@saxpath.org). + + In addition, we request (but do not require) that you include in the + end-user documentation provided with the redistribution and/or in the + software itself an acknowledgement equivalent to the following: + "This product includes software developed by the + SAXPath Project (http://www.saxpath.org/)." + Alternatively, the acknowledgment may be graphical using the logos + available at http://www.saxpath.org/ + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE SAXPath AUTHORS OR THE PROJECT + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + This software consists of voluntary contributions made by many + individuals on behalf of the SAXPath Project and was originally + created by bob mcwhirter and + James Strachan . For more information on the + SAXPath Project, please see . + + */ + +src/plugin/lib-xml/lib/xercesImpl.jar + +ASF + + + diff --git a/charts/open-webui/charts/tika/README.md b/charts/open-webui/charts/tika/README.md new file mode 100644 index 0000000..b5d023f --- /dev/null +++ b/charts/open-webui/charts/tika/README.md @@ -0,0 +1,146 @@ +tika-helm +========= + +![lint + install workflow](https://github.com/apache/tika-helm/actions/workflows/lint-test.yaml/badge.svg) + +A [Helm chart][] to deploy [Apache Tika][] on [Kubernetes][]. + + + +This Helm chart is a lightweight way to configure and run the official [apache/tika][] Docker image. + +We recommend that the Helm chart version is aligned to the version Tika (and subsequently the +version of the [Tika Docker image][]) you want to deploy. +This will ensure that you using a chart version that has been tested against the corresponding +production version. This will also ensure that the documentation and examples for the chart +will work with the version of Tika you are installing. + + +**Warning**: This branch is used for development, please use the [latest release][] for released version. + + + + + +- [Requirements](#requirements) +- [Installing](#installing) + - [Install released version using Helm repository](#install-released-version-using-helm-repository) + - [Install development version using master branch](#install-development-version-using-master-branch) +- [Upgrading](#upgrading) +- [Usage notes](#usage-notes) +- [Configuration](#configuration) + - [Deprecated](#deprecated) +- [FAQ](#faq) +- [Contributing](#contributing) + + + + + + +## Requirements + +* Kubernetes >= 1.14 +* [Helm][] >= v3.4.2 + +## Installing + +### Install released version using Helm repository + +**N.B.** You may or may not need/wish to install the chart into a specific **namespace**, +in which case you may need to augment the commands below. + +* Add the Tika Helm charts repo: +`helm repo add tika https://apache.jfrog.io/artifactory/tika` + +* Install it: + - with Helm 3: `helm install tika tika/tika --set image.tag=${release.version} -n tika-test`, you will see something like +``` +helm install tika tika/tika --set image.tag=latest-full -n tika-test + +... +NAME: tika +LAST DEPLOYED: Mon Jan 24 13:38:01 2022 +NAMESPACE: tika-test +STATUS: deployed +REVISION: 1 +NOTES: +1. Get the application URL by running these commands: + export POD_NAME=$(kubectl get pods --namespace tika-test -l "app.kubernetes.io/name=tika,app.kubernetes.io/instance=tika" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace tika-test $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:9998 to use your application" + kubectl --namespace tika-test port-forward $POD_NAME 9998:$CONTAINER_PORT +``` +You may notice that the _kubectl port forwarding_ experiences a _timeout issue_ which ultimately kills the app. In this case you can run port formarding in a loop +``` +while true; do kubectl --namespace tika-test port-forward $POD_NAME 9998:$CONTAINER_PORT ; done +``` +... this should keep `kubectl` reconnecting on connection lost. + +### Install development version using master branch + +* Clone the git repo: `git clone git@github.com:apache/tika-helm.git` + +* Install it: + - with Helm 3: `helm install tika . --set image.tag=latest-full` + +## Upgrading + +Please always check [CHANGELOG.md][] and [BREAKING_CHANGES.md][] before +upgrading to a new chart version. + + +## Usage notes + +* TODO + + +## Configuration + +| Parameter | Description | Default | +|--------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------| +| `...` | ... | ... | + +### Deprecated + +| Parameter | Description | Default | +|----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|---------| +| `...` | ... | `...` | + +## FAQ + +None yet... + +## Contributing + +Please check [CONTRIBUTING][] before any contribution or for any questions +about our development and testing process. + +## More Information + +For more infomation on Apache Tika Server, go to the [Apache Tika Server documentation][]. + +For more information on Apache Tika, go to the official [Apache Tika][] project website. + +For more information on the Apache Software Foundation, go to the [Apache Software Foundation][] website. + +## Authors + +Apache Tika Dev Team (dev@tika.apache.org) + +# License +The code is licensed permissively under the [Apache License v2.0][]. + +[Apache License v2.0]: https://www.apache.org/licenses/LICENSE-2.0.html +[Apache Software Foundation]: http://apache.org +[Apache Tika]: https://tika.apache.org +[Apache Tika Server documentation]: https://cwiki.apache.org/confluence/display/TIKA/TikaServer +[BREAKING_CHANGES.md]: https://github.com/apache/tika-helm/blob/master/BREAKING_CHANGES.md +[CHANGELOG.md]: https://github.com/apache/tika-helm/blob/master/CHANGELOG.md +[CONTRIBUTING]: https://github.com/apache/tika#contributing-via-github +[apache/tika]: https://github.com/apache/tika-docker +[Helm chart]: https://helm.sh/docs/topics/charts/ +[Kubernetes]: https://kubernetes.io/ +[Tika Docker image]: https://hub.docker.com/r/apache/tika/tags?page=1&ordering=last_updated +[helm]: https://helm.sh +[latest release]: https://github.com/apache/tika-helm/releases diff --git a/charts/open-webui/charts/tika/templates/NOTES.txt b/charts/open-webui/charts/tika/templates/NOTES.txt new file mode 100644 index 0000000..21fb4d6 --- /dev/null +++ b/charts/open-webui/charts/tika/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "tika-helm.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "tika-helm.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "tika-helm.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "tika-helm.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:9998 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 9998:$CONTAINER_PORT +{{- end }} diff --git a/charts/open-webui/charts/tika/templates/_helpers.tpl b/charts/open-webui/charts/tika/templates/_helpers.tpl new file mode 100644 index 0000000..da7b610 --- /dev/null +++ b/charts/open-webui/charts/tika/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "tika-helm.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 "tika-helm.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 "tika-helm.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "tika-helm.labels" -}} +helm.sh/chart: {{ include "tika-helm.chart" . }} +{{ include "tika-helm.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "tika-helm.selectorLabels" -}} +app.kubernetes.io/name: {{ include "tika-helm.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "tika-helm.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "tika-helm.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/charts/open-webui/charts/tika/templates/deployment.yaml b/charts/open-webui/charts/tika/templates/deployment.yaml new file mode 100644 index 0000000..44cec3e --- /dev/null +++ b/charts/open-webui/charts/tika/templates/deployment.yaml @@ -0,0 +1,95 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "tika-helm.fullname" . }} + labels: + {{- include "tika-helm.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "tika-helm.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "tika-helm.selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "tika-helm.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + volumes: + - name: sec-ctx-vol + emptyDir: {} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + volumeMounts: + - name: sec-ctx-vol + mountPath: /tmp + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + livenessProbe: + httpGet: + path: {{if .Values.config.base_url }}{{- with urlParse (tpl .Values.config.base_url .) }}{{ .path }}{{end}}{{else}}/{{end}} + port: {{ .Values.service.port }} + scheme: {{ .Values.livenessProbe.scheme | default "http" }} + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds | default 15 }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds | default 30 }} + failureThreshold: {{ .Values.livenessProbe.failureThreshold | default 20 }} + periodSeconds: {{ .Values.livenessProbe.periodSeconds | default 5 }} + readinessProbe: + httpGet: + path: {{if .Values.config.base_url }}{{- with urlParse (tpl .Values.config.base_url .) }}{{ .path }}{{end}}{{else}}/{{end}} + port: {{ .Values.service.port }} + scheme: {{ .Values.readinessProbe.scheme | default "http" }} + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds | default 15 }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds | default 30 }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold | default 20 }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds | default 5 }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/charts/open-webui/charts/tika/templates/hpa.yaml b/charts/open-webui/charts/tika/templates/hpa.yaml new file mode 100644 index 0000000..3c162ed --- /dev/null +++ b/charts/open-webui/charts/tika/templates/hpa.yaml @@ -0,0 +1,65 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +--- +{{- if .Values.autoscaling.enabled }} +apiVersion: {{ .Values.autoscaling.apiVersion }} +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "tika-helm.fullname" . }} + labels: + {{- include "tika-helm.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "tika-helm.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if eq .Values.autoscaling.apiVersion "autoscaling/v2" }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- else }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/open-webui/charts/tika/templates/ingress.yaml b/charts/open-webui/charts/tika/templates/ingress.yaml new file mode 100644 index 0000000..716fd6d --- /dev/null +++ b/charts/open-webui/charts/tika/templates/ingress.yaml @@ -0,0 +1,79 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +--- +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "tika-helm.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "tika-helm.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + 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 }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/open-webui/charts/tika/templates/networkpolicy.yaml b/charts/open-webui/charts/tika/templates/networkpolicy.yaml new file mode 100644 index 0000000..5a2089f --- /dev/null +++ b/charts/open-webui/charts/tika/templates/networkpolicy.yaml @@ -0,0 +1,23 @@ +{{- if .Values.networkPolicy.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ template "tika-helm.fullname" . }} + labels: + {{- include "tika-helm.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + {{- include "tika-helm.selectorLabels" . | nindent 6 }} + egress: + - {} + ingress: + - ports: + - port: {{ .Values.service.port }} + {{- if not .Values.networkPolicy.allowExternal }} + from: + - podSelector: + matchLabels: + {{ template "tika-helm.fullname" . }}-client: "true" + {{- end }} +{{- end }} diff --git a/charts/open-webui/charts/tika/templates/service.yaml b/charts/open-webui/charts/tika/templates/service.yaml new file mode 100644 index 0000000..d99b4b2 --- /dev/null +++ b/charts/open-webui/charts/tika/templates/service.yaml @@ -0,0 +1,33 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "tika-helm.fullname" . }} + labels: + {{- include "tika-helm.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "tika-helm.selectorLabels" . | nindent 4 }} diff --git a/charts/open-webui/charts/tika/templates/serviceaccount.yaml b/charts/open-webui/charts/tika/templates/serviceaccount.yaml new file mode 100644 index 0000000..0f708f2 --- /dev/null +++ b/charts/open-webui/charts/tika/templates/serviceaccount.yaml @@ -0,0 +1,30 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +--- +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "tika-helm.serviceAccountName" . }} + labels: + {{- include "tika-helm.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/open-webui/charts/tika/templates/tests/test-connection.yaml b/charts/open-webui/charts/tika/templates/tests/test-connection.yaml new file mode 100644 index 0000000..5de0a4c --- /dev/null +++ b/charts/open-webui/charts/tika/templates/tests/test-connection.yaml @@ -0,0 +1,33 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +--- +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "tika-helm.fullname" . }}-test-connection" + labels: + {{- include "tika-helm.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "tika-helm.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/charts/open-webui/charts/tika/values.yaml b/charts/open-webui/charts/tika/values.yaml new file mode 100644 index 0000000..8d38f46 --- /dev/null +++ b/charts/open-webui/charts/tika/values.yaml @@ -0,0 +1,67 @@ +affinity: {} +autoscaling: + apiVersion: autoscaling/v2beta1 + enabled: false + maxReplicas: 100 + minReplicas: 1 + targetCPUUtilizationPercentage: 80 + targetMemoryUtilizationPercentage: 80 +config: + base_url: http://localhost/ +fullnameOverride: "" +image: + pullPolicy: IfNotPresent + repository: apache/tika + tag: 2.9.0.0 +imagePullSecrets: [] +ingress: + annotations: {} + enabled: false + hosts: + - host: chart-example.local + paths: [] + tls: [] +livenessProbe: + failureThreshold: 20 + initialDelaySeconds: 15 + periodSeconds: 5 + scheme: HTTP + timeoutSeconds: 30 +nameOverride: "" +networkPolicy: + allowExternal: false + enabled: false +nodeSelector: {} +podAnnotations: {} +podSecurityContext: {} +readinessProbe: + failureThreshold: 20 + initialDelaySeconds: 15 + periodSeconds: 5 + scheme: HTTP + timeoutSeconds: 30 +replicaCount: 1 +resources: + limits: + cpu: "2" + memory: 2000Mi + requests: + cpu: "1" + memory: 1500Mi +securityContext: + allowPrivilegeEscalation: true + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsGroup: 35002 + runAsNonRoot: true + runAsUser: 35002 +service: + port: 9998 + type: ClusterIP +serviceAccount: + annotations: {} + create: true + name: "" +tolerations: [] diff --git a/charts/open-webui/custom-values.yaml b/charts/open-webui/custom-values.yaml new file mode 100644 index 0000000..2ca6316 --- /dev/null +++ b/charts/open-webui/custom-values.yaml @@ -0,0 +1,57 @@ +ollama: + enabled: false +tika: + enabled: false +websocket: + enabled: false +redis-cluster: + enabled: false +ingress: + enabled: true + annotations: + cert-manager.io/cluster-issuer: "root-ca-issuer" + host: "open-webui.example.com" + tls: true + existingSecret: "open-webui-tls-secret" +persistence: + enabled: true + size: 2Gi + existingClaim: "" + subPath: "" + accessModes: + - ReadWriteOnce + storageClass: "" + selector: {} + annotations: {} +extraEnvVars: + - name: OPENAI_API_KEY + value: "0p3n-w3bu!" + - name: OAUTH_CLIENT_ID + value: open-webui + - name: OAUTH_CLIENT_SECRET + value: UXVwsmPSbWZ0pZkxwdgeY88vl9JJZJLQ + - name: OPENID_PROVIDER_URL + value: https://keycloak.examole.com/auth/realms/paasup/.well-known/openid-configuration + - name: OAUTH_PROVIDER_NAME + value: paasup + - name: OAUTH_SCOPES + value: 'openid email profile' + - name: ENABLE_LOGIN_FORM + value: 'true' + - name: SSL_CERT_FILE + value: '/etc/ssl/certs/keycloak/ca.crt' + - name: ENABLE_OAUTH_SIGNUP + value: 'true' + - name: OAUTH_MERGE_ACCOUNTS_BY_EMAIL + value: 'true' + +volumeMounts: + initContainer: [] + container: + - name: "keycloak-tls" + mountPath: "/etc/ssl/certs/keycloak" + +volumes: +- name: "keycloak-tls" + secret: + secretName: keycloak-tls-secret diff --git a/charts/open-webui/templates/NOTES.txt b/charts/open-webui/templates/NOTES.txt new file mode 100644 index 0000000..1fe5e46 --- /dev/null +++ b/charts/open-webui/templates/NOTES.txt @@ -0,0 +1,77 @@ +{{- ` +🎉 Welcome to Open WebUI!! + ___ __ __ _ _ _ ___ + / _ \ _ __ ___ _ __ \ \ / /__| |__ | | | |_ _| +| | | | '_ \ / _ \ '_ \ \ \ /\ / / _ \ '_ \| | | || | +| |_| | |_) | __/ | | | \ V V / __/ |_) | |_| || | + \___/| .__/ \___|_| |_| \_/\_/ \___|_.__/ \___/|___| + |_| +` }} +v{{ .Chart.AppVersion }} - building the best open-source AI user interface. + - Chart Version: v{{ .Chart.Version }} + - Project URL 1: {{ .Chart.Home }} + - Project URL 2: https://github.com/open-webui/open-webui + - Documentation: https://docs.openwebui.com/ + - Chart URL: https://github.com/open-webui/helm-charts + +Open WebUI is a web-based user interface that works with Ollama, OpenAI, Claude 3, Gemini and more. +This interface allows you to easily interact with local AI models. + +1. Deployment Information: + - Chart Name: {{ .Chart.Name }} + - Release Name: {{ .Release.Name }} + - Namespace: {{ .Release.Namespace }} + +2. Access the Application: +{{- if contains "ClusterIP" .Values.service.type }} + Access via ClusterIP service: + + export LOCAL_PORT=8080 + export POD_NAME=$(kubectl get pods -n {{ .Release.Namespace }} -l "app.kubernetes.io/component={{ include "open-webui.name" . }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod -n {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + kubectl -n {{ .Release.Namespace }} port-forward $POD_NAME $LOCAL_PORT:$CONTAINER_PORT + echo "Visit http://127.0.0.1:$LOCAL_PORT to use your application" + + Then, access the application at: http://127.0.0.1:$LOCAL_PORT or http://localhost:8080 + +{{- else if contains "NodePort" .Values.service.type }} + Access via NodePort service: + export NODE_PORT=$(kubectl get -n {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "open-webui.name" . }}) + export NODE_IP=$(kubectl get nodes -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT + +{{- else if contains "LoadBalancer" .Values.service.type }} + Access via LoadBalancer service: + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + NOTE: The external address format depends on your cloud provider: + - AWS: Will return a hostname (e.g., xxx.elb.amazonaws.com) + - GCP/Azure: Will return an IP address + You can watch the status by running: + + kubectl get -n {{ .Release.Namespace }} svc {{ include "open-webui.name" . }} --watch + export EXTERNAL_IP=$(kubectl get -n {{ .Release.Namespace }} svc {{ include "open-webui.name" . }} -o jsonpath="{.status.loadBalancer.ingress[0].hostname:-.status.loadBalancer.ingress[0].ip}") + echo http://$EXTERNAL_IP:{{ .Values.service.port }} +{{- end }} + +{{- if .Values.ingress.enabled }} + + Ingress is enabled. Access the application at: http{{ if .Values.ingress.tls }}s{{ end }}://{{ .Values.ingress.host }} +{{- end }} + +3. Useful Commands: + - Check deployment status: + helm status {{ .Release.Name }} -n {{ .Release.Namespace }} + + - Get detailed information: + helm get all {{ .Release.Name }} -n {{ .Release.Namespace }} + + - View logs: + {{- if .Values.persistence.enabled }} + kubectl logs -f statefulset/{{ include "open-webui.name" . }} -n {{ .Release.Namespace }} + {{- else }} + kubectl logs -f deployment/{{ include "open-webui.name" . }} -n {{ .Release.Namespace }} + {{- end }} + +4. Cleanup: + - Uninstall the deployment: + helm uninstall {{ .Release.Name }} -n {{ .Release.Namespace }} diff --git a/charts/open-webui/templates/_helpers.tpl b/charts/open-webui/templates/_helpers.tpl new file mode 100644 index 0000000..953244b --- /dev/null +++ b/charts/open-webui/templates/_helpers.tpl @@ -0,0 +1,171 @@ +{{/* +Allow the release namespace to be overridden for multi-namespace deployments in combined charts +*/}} +{{- define "open-webui.namespace" -}} + {{- if .Values.namespaceOverride -}} + {{- .Values.namespaceOverride -}} + {{- else -}} + {{- .Release.Namespace -}} + {{- end -}} +{{- end -}} + +{{/* +Set the name of the Open WebUI resources +*/}} +{{- define "open-webui.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end -}} + +{{/* +Set the name of the integrated Ollama resources +*/}} +{{- define "ollama.name" -}} +open-webui-ollama +{{- end -}} + +{{/* +Set the name of the integrated Pipelines resources +*/}} +{{- define "pipelines.name" -}} +open-webui-pipelines +{{- end -}} + +{{/* +Constructs a semicolon-separated string of Ollama API endpoint URLs from the ollamaUrls list +defined in the values.yaml file +*/}} +{{- define "ollamaUrls" -}} +{{- if .Values.ollamaUrls }} +{{- join ";" .Values.ollamaUrls | trimSuffix "/" }} +{{- end }} +{{- end }} + +{{/* +Generates the URL for accessing the Ollama service within the Kubernetes cluster when the +ollama.enabled value is set to true, which means that the Ollama Helm chart is being installed +as a dependency of the Open WebUI chart +*/}} +{{- define "ollamaLocalUrl" -}} +{{- if .Values.ollama.enabled -}} +{{- $clusterDomain := .Values.clusterDomain }} +{{- $ollamaServicePort := .Values.ollama.service.port | toString }} +{{- printf "http://%s.%s.svc.%s:%s" (default .Values.ollama.name .Values.ollama.fullnameOverride) (.Release.Namespace) $clusterDomain $ollamaServicePort }} +{{- end }} +{{- end }} + +{{/* +Constructs a string containing the URLs of the Ollama API endpoints that the Open WebUI +application should use based on which values are set for Ollama/ whether the Ollama +subchart is in use +*/}} +{{- define "ollamaBaseUrls" -}} +{{- $ollamaLocalUrl := include "ollamaLocalUrl" . }} +{{- $ollamaUrls := include "ollamaUrls" . }} +{{- if and .Values.ollama.enabled .Values.ollamaUrls }} +{{- printf "%s;%s" $ollamaUrls $ollamaLocalUrl }} +{{- else if .Values.ollama.enabled }} +{{- printf "%s" $ollamaLocalUrl }} +{{- else if .Values.ollamaUrls }} +{{- printf "%s" $ollamaUrls }} +{{- end }} +{{- end }} + +{{/* +Create the chart name and version for the chart label +*/}} +{{- define "chart.name" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create the base labels to include on chart resources +*/}} +{{- define "base.labels" -}} +helm.sh/chart: {{ include "chart.name" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Create selector labels to include on all resources +*/}} +{{- define "base.selectorLabels" -}} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{/* +Create selector labels to include on all Open WebUI resources +*/}} +{{- define "open-webui.selectorLabels" -}} +{{ include "base.selectorLabels" . }} +app.kubernetes.io/component: {{ .Chart.Name }} +{{- end }} + +{{/* +Create labels to include on chart all Open WebUI resources +*/}} +{{- define "open-webui.labels" -}} +{{ include "base.labels" . }} +{{ include "open-webui.selectorLabels" . }} +{{- end }} + +{{/* +Create selector labels to include on chart all Ollama resources +*/}} +{{- define "ollama.selectorLabels" -}} +{{ include "base.selectorLabels" . }} +app.kubernetes.io/component: {{ include "ollama.name" . }} +{{- end }} + +{{/* +Create labels to include on chart all Ollama resources +*/}} +{{- define "ollama.labels" -}} +{{ include "base.labels" . }} +{{ include "ollama.selectorLabels" . }} +{{- end }} + +{{/* +Create selector labels to include on chart all Pipelines resources +*/}} +{{- define "pipelines.selectorLabels" -}} +{{ include "base.selectorLabels" . }} +app.kubernetes.io/component: {{ include "pipelines.name" . }} +{{- end }} + +{{/* +Create labels to include on chart all Pipelines resources +*/}} +{{- define "pipelines.labels" -}} +{{ include "base.labels" . }} +{{ include "pipelines.selectorLabels" . }} +{{- end }} + +{{/* +Create the service endpoint to use for Pipelines if the subchart is used +*/}} +{{- define "pipelines.serviceEndpoint" -}} +{{- if .Values.pipelines.enabled -}} +{{- $clusterDomain := .Values.clusterDomain }} +{{- $pipelinesServicePort := .Values.pipelines.service.port | toString }} +{{- printf "http://%s.%s.svc.%s:%s" (include "pipelines.name" .) (.Release.Namespace) $clusterDomain $pipelinesServicePort }} +{{- end }} +{{- end }} + +{{/* +Create selector labels to include on chart all websocket resources +*/}} +{{- define "websocket.redis.selectorLabels" -}} +{{ include "base.selectorLabels" . }} +app.kubernetes.io/component: {{ .Values.websocket.redis.name }} +{{- end }} + +{{/* +Create labels to include on chart all websocket resources +*/}} +{{- define "websocket.redis.labels" -}} +{{ include "base.labels" . }} +{{ include "websocket.redis.selectorLabels" . }} +{{- end }} diff --git a/charts/open-webui/templates/extra-resources.yaml b/charts/open-webui/templates/extra-resources.yaml new file mode 100644 index 0000000..5124a0b --- /dev/null +++ b/charts/open-webui/templates/extra-resources.yaml @@ -0,0 +1,6 @@ +{{- if .Values.extraResources }} +{{- range .Values.extraResources }} +--- +{{ toYaml . | nindent 0 }} +{{- end }} +{{- end }} diff --git a/charts/open-webui/templates/ingress.yaml b/charts/open-webui/templates/ingress.yaml new file mode 100644 index 0000000..6b89ff4 --- /dev/null +++ b/charts/open-webui/templates/ingress.yaml @@ -0,0 +1,49 @@ +{{- if .Values.ingress.enabled }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "open-webui.name" . }} + namespace: {{ include "open-webui.namespace" . }} + labels: + {{- include "open-webui.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with .Values.ingress.class }} + ingressClassName: {{ . }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + - hosts: + - {{ .Values.ingress.host | quote }} + {{- range .Values.ingress.additionalHosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ default (printf "%s-tls" .Release.Name) .Values.ingress.existingSecret }} + {{- end }} + rules: + - host: {{ .Values.ingress.host }} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ include "open-webui.name" . }} + port: + name: http + {{- range .Values.ingress.additionalHosts }} + - host: {{ . | quote }} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ include "open-webui.name" $ }} + port: + name: http + {{- end }} +{{- end }} diff --git a/charts/open-webui/templates/pvc.yaml b/charts/open-webui/templates/pvc.yaml new file mode 100644 index 0000000..c412e7f --- /dev/null +++ b/charts/open-webui/templates/pvc.yaml @@ -0,0 +1,28 @@ +{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "open-webui.name" . }} + namespace: {{ include "open-webui.namespace" . }} + labels: + {{- include "open-webui.selectorLabels" . | nindent 4 }} + {{- with .Values.persistence.annotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} +spec: + accessModes: + {{- range .Values.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size }} + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass }} + {{- end }} + {{- with .Values.persistence.selector }} + selector: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/open-webui/templates/service-account.yaml b/charts/open-webui/templates/service-account.yaml new file mode 100644 index 0000000..ecea069 --- /dev/null +++ b/charts/open-webui/templates/service-account.yaml @@ -0,0 +1,14 @@ +{{- if .Values.serviceAccount.enable }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Values.serviceAccount.name | default (include "open-webui.name" .) }} + namespace: {{ include "open-webui.namespace" . }} + labels: + {{- include "open-webui.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} +{{- end }} \ No newline at end of file diff --git a/charts/open-webui/templates/service.yaml b/charts/open-webui/templates/service.yaml new file mode 100644 index 0000000..bce51b6 --- /dev/null +++ b/charts/open-webui/templates/service.yaml @@ -0,0 +1,36 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "open-webui.name" . }} + namespace: {{ include "open-webui.namespace" . }} + labels: + {{- include "open-webui.labels" . | nindent 4 }} + {{- with .Values.service.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + selector: + {{- include "open-webui.selectorLabels" . | nindent 4 }} + type: {{ .Values.service.type | default "ClusterIP" }} + ports: + - protocol: TCP + name: http + port: {{ .Values.service.port }} + targetPort: http + {{- if .Values.service.nodePort }} + nodePort: {{ .Values.service.nodePort | int }} + {{- end }} + {{- if .Values.service.loadBalancerClass }} + loadBalancerClass: {{ .Values.service.loadBalancerClass | quote }} + {{- end }} + {{- if and (eq .Values.service.type "ClusterIP") (.Values.service.clusterIP) }} + clusterIP: {{ .Values.service.clusterIP }} + {{- end }} + {{- if and (eq .Values.service.type "LoadBalancer") (.Values.service.loadBalancerIP) }} + loadBalancerIP: {{ .Values.service.loadBalancerIP }} + {{- end }} + diff --git a/charts/open-webui/templates/websocket-redis.yaml b/charts/open-webui/templates/websocket-redis.yaml new file mode 100644 index 0000000..5e95dc8 --- /dev/null +++ b/charts/open-webui/templates/websocket-redis.yaml @@ -0,0 +1,71 @@ +{{- if and .Values.websocket.enabled .Values.websocket.redis.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Values.websocket.redis.name }} + namespace: {{ include "open-webui.namespace" . }} + labels: + {{- include "websocket.redis.labels" . | nindent 4 }} + {{- with .Values.websocket.redis.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + annotations: + {{- with .Values.websocket.redis.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + selector: + matchLabels: + {{- include "websocket.redis.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "websocket.redis.labels" . | nindent 8 }} + spec: + containers: + - name: {{ .Values.websocket.redis.name }} + image: "{{ .Values.websocket.redis.image.repository }}:{{ .Values.websocket.redis.image.tag }}" + imagePullPolicy: {{ .Values.websocket.redis.image.pullPolicy }} + {{- with .Values.websocket.redis.command }} + command: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.websocket.redis.args }} + args: + {{- toYaml . | nindent 10 }} + {{- end }} + ports: + - name: http + containerPort: {{ .Values.websocket.redis.service.containerPort }} + {{- with .Values.websocket.redis.resources }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Values.websocket.redis.name }} + namespace: {{ include "open-webui.namespace" . }} + labels: + {{- include "websocket.redis.labels" . | nindent 4 }} + {{- with .Values.websocket.redis.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + annotations: + {{- with .Values.websocket.redis.service.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + selector: + {{- include "websocket.redis.selectorLabels" . | nindent 4 }} + type: {{ .Values.websocket.redis.service.type | default "ClusterIP" }} + ports: + - protocol: TCP + name: http + port: {{ .Values.websocket.redis.service.port }} + targetPort: http + {{- if .Values.websocket.redis.service.nodePort }} + nodePort: {{ .Values.websocket.redis.service.nodePort | int }} + {{- end }} +{{- end }} diff --git a/charts/open-webui/templates/workload-manager.yaml b/charts/open-webui/templates/workload-manager.yaml new file mode 100644 index 0000000..97fcabe --- /dev/null +++ b/charts/open-webui/templates/workload-manager.yaml @@ -0,0 +1,178 @@ +apiVersion: apps/v1 +{{- if .Values.persistence.enabled }} +kind: StatefulSet +{{- else }} +kind: Deployment +{{- end }} +metadata: + name: {{ include "open-webui.name" . }} + namespace: {{ include "open-webui.namespace" . }} + labels: + {{- include "open-webui.labels" . | nindent 4 }} + {{- with .Values.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.replicaCount }} + {{- if .Values.persistence.enabled }} + serviceName: {{ include "open-webui.name" . }} + {{- end }} + selector: + matchLabels: + {{- include "open-webui.selectorLabels" . | nindent 6 }} + {{- if .Values.strategy }} + {{- if .Values.persistence.enabled }} + updateStrategy: + {{- toYaml .Values.strategy | nindent 4 }} + {{- else }} + strategy: + {{- toYaml .Values.strategy | nindent 4 }} + {{- end }} + {{- end }} + template: + metadata: + labels: + {{- include "open-webui.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + initContainers: + - name: copy-app-data + {{- with .Values.image }} + image: {{ .repository }}:{{ .tag | default $.Chart.AppVersion }} + imagePullPolicy: {{ .pullPolicy }} + {{- end }} + command: ['sh', '-c', 'cp -R -n /app/backend/data/* /tmp/app-data/'] + {{- with .Values.containerSecurityContext }} + securityContext: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.copyAppData.resources }} + resources: {{- toYaml . | nindent 10 }} + {{- end }} + volumeMounts: + - name: data + mountPath: /tmp/app-data + {{- if .Values.persistence.subPath }} + subPath: {{ .Values.persistence.subPath }} + {{- end }} + {{- with .Values.volumeMounts.initContainer }} + {{- toYaml . | nindent 8 }} + {{- end }} + enableServiceLinks: false + automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} + {{- if .Values.serviceAccount.enable }} + serviceAccountName: {{ .Values.serviceAccount.name | default (include "open-webui.name" .) }} + {{- end }} + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: {{ .Chart.Name }} + {{- with .Values.image }} + image: {{ .repository }}:{{ .tag | default $.Chart.AppVersion }} + imagePullPolicy: {{ .pullPolicy }} + {{- end }} + ports: + - name: http + containerPort: {{ .Values.service.containerPort }} + {{- with .Values.livenessProbe }} + livenessProbe: {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.readinessProbe }} + readinessProbe: {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.startupProbe }} + startupProbe: {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.resources }} + resources: {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.containerSecurityContext }} + securityContext: + {{- toYaml . | nindent 10 }} + {{- end }} + volumeMounts: + - name: data + mountPath: /app/backend/data + {{- if .Values.persistence.subPath }} + subPath: {{ .Values.persistence.subPath }} + {{- end }} + {{- with .Values.volumeMounts.container }} + {{- toYaml . | nindent 8 }} + {{- end }} + env: + {{- if or .Values.ollamaUrls .Values.ollama.enabled }} + - name: "OLLAMA_BASE_URLS" + value: {{ include "ollamaBaseUrls" . | quote }} + {{- else }} + - name: "ENABLE_OLLAMA_API" + value: "False" + {{- end }} + - name: "OPENAI_API_BASE_URL" + {{- if .Values.pipelines.enabled }} + value: {{ include "pipelines.serviceEndpoint" . }} + {{- else if .Values.openaiBaseApiUrl }} + value: {{ .Values.openaiBaseApiUrl | quote }} + {{- end }} + {{- if .Values.extraEnvVars }} + {{- toYaml .Values.extraEnvVars | nindent 8 }} + {{- end }} + {{- if .Values.tika.enabled }} + - name: "CONTENT_EXTRACTION_ENGINE" + value: "Tika" + - name: "TIKA_SERVER_URL" + value: http://{{ .Chart.Name }}-tika:9998 + {{- end }} + {{- if .Values.websocket.enabled }} + - name: "ENABLE_WEBSOCKET_SUPPORT" + value: "True" + - name: "WEBSOCKET_MANAGER" + value: {{ .Values.websocket.manager | default "redis" | quote }} + - name: "WEBSOCKET_REDIS_URL" + value: {{ .Values.websocket.url | quote }} + {{- end }} + tty: true + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + {{- if and .Values.persistence.enabled .Values.persistence.existingClaim }} + - name: data + persistentVolumeClaim: + claimName: {{ .Values.persistence.existingClaim }} + {{- else if not .Values.persistence.enabled }} + - name: data + emptyDir: {} + {{- else if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} + - name: data + persistentVolumeClaim: + claimName: {{ include "open-webui.name" . }} + {{- end }} + {{- with .Values.volumes }} + {{- toYaml . | nindent 6 }} + {{- end }} diff --git a/charts/open-webui/values.yaml b/charts/open-webui/values.yaml new file mode 100644 index 0000000..77bc2e6 --- /dev/null +++ b/charts/open-webui/values.yaml @@ -0,0 +1,276 @@ +nameOverride: "" +namespaceOverride: "" + +ollama: + # -- Automatically install Ollama Helm chart from https://otwld.github.io/ollama-helm/. Use [Helm Values](https://github.com/otwld/ollama-helm/#helm-values) to configure + enabled: true + # -- If enabling embedded Ollama, update fullnameOverride to your desired Ollama name value, or else it will use the default ollama.name value from the Ollama chart + fullnameOverride: "open-webui-ollama" + # -- Example Ollama configuration with nvidia GPU enabled, automatically downloading a model, and deploying a PVC for model persistence + # ollama: + # gpu: + # enabled: true + # type: 'nvidia' + # number: 1 + # models: + # - llama3 + # runtimeClassName: nvidia + # persistentVolume: + # enabled: true + # volumeName: "example-pre-existing-pv-created-by-smb-csi" + +pipelines: + # -- Automatically install Pipelines chart to extend Open WebUI functionality using Pipelines: https://github.com/open-webui/pipelines + enabled: true + # -- This section can be used to pass required environment variables to your pipelines (e.g. Langfuse hostname) + extraEnvVars: [] + +tika: + # -- Automatically install Apache Tika to extend Open WebUI + enabled: false + +# -- A list of Ollama API endpoints. These can be added in lieu of automatically installing the Ollama Helm chart, or in addition to it. +ollamaUrls: [] + +websocket: + # -- Enables websocket support in Open WebUI with env `ENABLE_WEBSOCKET_SUPPORT` + enabled: false + # -- Specifies the websocket manager to use with env `WEBSOCKET_MANAGER`: redis (default) + manager: redis + # -- Specifies the URL of the Redis instance for websocket communication. Template with `redis://[:@]:/` + url: redis://open-webui-redis:6379/0 + # -- Deploys a redis + redis: + # -- Enable redis installation + enabled: true + # -- Redis name + name: open-webui-redis + # -- Redis labels + labels: {} + # -- Redis annotations + annotations: {} + # -- Redis image + image: + repository: redis + tag: 7.4.2-alpine3.21 + pullPolicy: IfNotPresent + # -- Redis command (overrides default) + command: [] + # -- Redis arguments (overrides default) + args: [] + # -- Redis resources + resources: {} + # -- Redis service + service: + # -- Redis container/target port + containerPort: 6379 + # -- Redis service type + type: ClusterIP + # -- Redis service labels + labels: {} + # -- Redis service annotations + annotations: {} + # -- Redis service port + port: 6379 + # -- Redis service node port. Valid only when type is `NodePort` + nodePort: "" + +# -- Deploys a Redis cluster with subchart 'redis' from bitnami +redis-cluster: + # -- Enable Redis installation + enabled: false + # -- Redis cluster name (recommended to be 'open-webui-redis') + # - In this case, redis url will be 'redis://open-webui-redis-master:6379/0' or 'redis://[:@]open-webui-redis-master:6379/0' + fullnameOverride: open-webui-redis + # -- Redis Authentication + auth: + # -- Enable Redis authentication (disabled by default). For your security, we strongly suggest that you switch to 'auth.enabled=true' + enabled: false + # -- Replica configuration for the Redis cluster + replica: + # -- Number of Redis replica instances + replicaCount: 3 + +# -- Value of cluster domain +clusterDomain: cluster.local + +annotations: {} +podAnnotations: {} +podLabels: {} +replicaCount: 1 +# -- Strategy for updating the workload manager: deployment or statefulset +strategy: {} +# -- Open WebUI image tags can be found here: https://github.com/open-webui/open-webui +image: + repository: ghcr.io/open-webui/open-webui + tag: "" + pullPolicy: "IfNotPresent" + +serviceAccount: + enable: true + name: "" + annotations: {} + automountServiceAccountToken: false + +# -- Configure imagePullSecrets to use private registry +# ref: +imagePullSecrets: [] +# imagePullSecrets: +# - name: myRegistryKeySecretName + +# -- Probe for liveness of the Open WebUI container +# ref: +livenessProbe: {} +# livenessProbe: +# httpGet: +# path: /health +# port: http +# failureThreshold: 1 +# periodSeconds: 10 + +# -- Probe for readiness of the Open WebUI container +# ref: +readinessProbe: {} +# readinessProbe: +# httpGet: +# path: /health/db +# port: http +# failureThreshold: 1 +# periodSeconds: 10 + +# -- Probe for startup of the Open WebUI container +# ref: +startupProbe: {} +# startupProbe: +# httpGet: +# path: /health +# port: http +# initialDelaySeconds: 30 +# periodSeconds: 5 +# failureThreshold: 20 + +resources: {} + +copyAppData: + resources: {} + +ingress: + enabled: false + class: "" + # -- Use appropriate annotations for your Ingress controller, e.g., for NGINX: + # nginx.ingress.kubernetes.io/rewrite-target: / + annotations: {} + host: "" + additionalHosts: [] + tls: false + existingSecret: "" +persistence: + enabled: true + size: 2Gi + # -- Use existingClaim if you want to re-use an existing Open WebUI PVC instead of creating a new one + existingClaim: "" + # -- Subdirectory of Open WebUI PVC to mount. Useful if root directory is not empty. + subPath: "" + # -- If using multiple replicas, you must update accessModes to ReadWriteMany + accessModes: + - ReadWriteOnce + storageClass: "" + selector: {} + annotations: {} + +# -- Node labels for pod assignment. +nodeSelector: {} + +# -- Tolerations for pod assignment +tolerations: [] + +# -- Affinity for pod assignment +affinity: {} + +# -- Topology Spread Constraints for pod assignment +topologySpreadConstraints: [] + +# -- Service values to expose Open WebUI pods to cluster +service: + type: ClusterIP + annotations: {} + port: 80 + containerPort: 8080 + nodePort: "" + labels: {} + loadBalancerClass: "" + +# -- OpenAI base API URL to use. Defaults to the Pipelines service endpoint when Pipelines are enabled, and "https://api.openai.com/v1" if Pipelines are not enabled and this value is blank +openaiBaseApiUrl: "" + +# -- Env vars added to the Open WebUI deployment. Most up-to-date environment variables can be found here: https://docs.openwebui.com/getting-started/env-configuration/ +extraEnvVars: + # -- Default API key value for Pipelines. Should be updated in a production deployment, or be changed to the required API key if not using Pipelines + - name: OPENAI_API_KEY + value: "0p3n-w3bu!" + # valueFrom: + # secretKeyRef: + # name: pipelines-api-key + # key: api-key + # - name: OPENAI_API_KEY + # valueFrom: + # secretKeyRef: + # name: openai-api-key + # key: api-key + # - name: OLLAMA_DEBUG + # value: "1" + +# -- Configure container volume mounts +# ref: +volumeMounts: + initContainer: [] + # - name: "" + # mountPath: "" + container: [] + # - name: "" + # mountPath: "" + +# -- Configure pod volumes +# ref: +volumes: [] +# - name: "" +# configMap: +# name: "" +# - name: "" +# emptyDir: {} + +# -- Configure pod security context +# ref: +podSecurityContext: + {} + # fsGroupChangePolicy: Always + # sysctls: [] + # supplementalGroups: [] + # fsGroup: 1001 + + +# -- Configure container security context +# ref: +containerSecurityContext: + {} + # runAsUser: 1001 + # runAsGroup: 1001 + # runAsNonRoot: true + # privileged: false + # allowPrivilegeEscalation: false + # readOnlyRootFilesystem: false + # capabilities: + # drop: + # - ALL + # seccompProfile: + # type: "RuntimeDefault" + +# -- Extra resources to deploy with Open WebUI +extraResources: + [] + # - apiVersion: v1 + # kind: ConfigMap + # metadata: + # name: example-configmap + # data: + # example-key: example-value