diff --git a/incubator/couchdb/.helmignore b/incubator/couchdb/.helmignore new file mode 100644 index 0000000000..f0c1319444 --- /dev/null +++ b/incubator/couchdb/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/incubator/couchdb/Chart.yaml b/incubator/couchdb/Chart.yaml new file mode 100644 index 0000000000..2a01f5242a --- /dev/null +++ b/incubator/couchdb/Chart.yaml @@ -0,0 +1,13 @@ +name: couchdb +version: 0.1.0 +description: A database featuring seamless multi-master sync, that scales from + big data to mobile, with an intuitive HTTP/JSON API and designed for + reliability. +home: https://couchdb.apache.org/ +sources: + - https://github.com/apache/couchdb + - https://github.com/kocolosk/couchdb-statefulset-assembler +maintainers: + - name: kocolosk + email: kocolosk@apache.org +icon: http://couchdb.apache.org/CouchDB-visual-identity/logo/CouchDB-couch-symbol.svg diff --git a/incubator/couchdb/README.md b/incubator/couchdb/README.md new file mode 100644 index 0000000000..56cd3310b8 --- /dev/null +++ b/incubator/couchdb/README.md @@ -0,0 +1,109 @@ +# CouchDB + +Apache CouchDB is a database featuring seamless multi-master sync, that scales +from big data to mobile, with an intuitive HTTP/JSON API and designed for +reliability. + +This chart deploys a CouchDB cluster as a StatefulSet. It creates a ClusterIP +Service in front of the deployment for load balancing by default, but can also +be configured to deploy other Service types or an Ingress controller. The +default persistence mechanism is simply the ephemeral local filesystem, but +production deployments should set `persistentVolume.enabled` to `true` to attach +storage volumes to each Pod in the deployment. + +## TL;DR + +```bash +$ helm install incubator/couchdb --set allowAdminParty=true +``` + +## Prerequisites + +- Kubernetes 1.8+ with Beta APIs enabled + +## Installing the Chart + +To install the chart with the release name `my-release`: + +```bash +$ helm repo add incubator http://storage.googleapis.com/kubernetes-charts-incubator +$ helm install --name my-release incubator/couchdb +``` + +This will create a Secret containing the admin credentials for the cluster. +Those credentials can be retrieved as follows: + +```bash +$ kubectl get secret my-release-couchdb -o go-template='{{ .data.adminPassword }}' | base64 --decode +``` + +If you prefer to configure the admin credentials directly you can create a +secret containing `adminUsername`, `adminPassword` and `cookieAuthSecret` keys: + +```bash +$ kubectl create secret generic my-release-couchdb --from-literal=adminUsername=foo --from-literal=adminPassword=bar --from-literal=cookieAuthSecret=baz +``` + +and then install the chart while overriding the `createAdminSecret` setting: + +```bash +$ helm install --name my-release --set createAdminSecret=false incubator/couchdb +``` + +This Helm chart deploys CouchDB on the Kubernetes cluster in a default +configuration. The [configuration](#configuration) section lists +the parameters that can be configured during installation. + +> **Tip**: List all releases using `helm list` + +## Uninstalling the Chart + +To uninstall/delete the `my-release` deployment: + +```bash +$ helm delete my-release +``` + +The command removes all the Kubernetes components associated with the chart and +deletes the release. + +## Configuration + +The following tables lists the most commonly configured parameters of the +CouchDB chart and their default values: + +| Parameter | Description | Default | +|---------------------------------|-------------------------------------------------------|----------------------------------------| +| `clusterSize` | The initial number of nodes in the CouchDB cluster | 3 | +| `couchdbConfig` | Map allowing override elements of server .ini config | {} | +| `allowAdminParty` | If enabled, start cluster without admin account | false (requires creating a Secret) | +| `createAdminSecret` | If enabled, create an admin account and cookie secret | true | +| `erlangFlags` | Map of flags supplied to the underlying Erlang VM | name: couchdb, setcookie: monster +| `persistentVolume.enabled` | Boolean determining whether to attach a PV to each node | false +| `persistentVolume.size` | If enabled, the size of the persistent volume to attach | 10Gi + +A variety of other parameters are also configurable. See the comments in the +`values.yaml` file for further details: + +| Parameter | Default | +|---------------------------------|----------------------------------------| +| `adminUsername` | admin | +| `adminPassword` | auto-generated | +| `cookieAuthSecret | auto-generated | +| `helperImage.repository` | kocolosk/couchdb-statefulset-assembler | +| `helperImage.tag` | 0.1.0 | +| `helperImage.pullPolicy` | IfNotPresent | +| `image.repository` | couchdb | +| `image.tag` | 2.1.1 | +| `image.pullPolicy` | IfNotPresent | +| `ingress.enabled` | false | +| `ingress.hosts` | chart-example.local | +| `ingress.annotations` | | +| `ingress.tls` | | +| `persistentVolume.accessModes` | ReadWriteOnce | +| `persistentVolume.storageClass` | Default for the Kube cluster | +| `podManagementPolicy` | Parallel | +| `resources` | | +| `service.enabled` | true | +| `service.type` | ClusterIP | +| `service.externalPort` | 5984 | diff --git a/incubator/couchdb/templates/NOTES.txt b/incubator/couchdb/templates/NOTES.txt new file mode 100644 index 0000000000..63b6436763 --- /dev/null +++ b/incubator/couchdb/templates/NOTES.txt @@ -0,0 +1,19 @@ +Apache CouchDB is starting. Check the status of the Pods using: + + kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "couchdb.name" . }},release={{ .Release.Name }}" + +Once all of the Pods are fully Ready, execute the following command to create +some required system databases: + + kubectl exec {{ if not .Values.allowAdminParty }}-it {{ end }}{{ template "couchdb.fullname" . }}-0 -c couchdb -- curl -s \ + http://127.0.0.1:5984/_cluster_setup \ + -X POST \ + -H "Content-Type: application/json" \ +{{- if .Values.allowAdminParty }} + -d '{"action": "finish_cluster"}' +{{- else }} + -d '{"action": "finish_cluster"}' \ + -u +{{- end }} + +Then it's time to relax. diff --git a/incubator/couchdb/templates/_helpers.tpl b/incubator/couchdb/templates/_helpers.tpl new file mode 100644 index 0000000000..9586171b3b --- /dev/null +++ b/incubator/couchdb/templates/_helpers.tpl @@ -0,0 +1,36 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "couchdb.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). +*/}} +{{- define "couchdb.fullname" -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +In the event that we create both a headless service and a traditional one, +ensure that the latter gets a unique name. +*/}} +{{- define "couchdb.svcname" -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- printf "%s-svc-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a random string if the supplied key does not exist +*/}} +{{- define "couchdb.defaultsecret" -}} +{{- if . -}} +{{- . | b64enc | quote -}} +{{- else -}} +{{- randAlphaNum 10 | b64enc | quote -}} +{{- end -}} +{{- end -}} diff --git a/incubator/couchdb/templates/configmap.yaml b/incubator/couchdb/templates/configmap.yaml new file mode 100644 index 0000000000..970078ca6f --- /dev/null +++ b/incubator/couchdb/templates/configmap.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "couchdb.fullname" . }} + labels: + app: {{ template "couchdb.name" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + heritage: {{ .Release.Service | quote }} + release: {{ .Release.Name | quote }} +data: + inifile: | + {{ range $section, $settings := .Values.couchdbConfig -}} + {{ printf "[%s]" $section }} + {{ range $key, $value := $settings -}} + {{ printf "%s = %s" $key ($value | toString) }} + {{ end }} + {{ end }} diff --git a/incubator/couchdb/templates/headless.yaml b/incubator/couchdb/templates/headless.yaml new file mode 100644 index 0000000000..23af20e848 --- /dev/null +++ b/incubator/couchdb/templates/headless.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "couchdb.fullname" . }} + labels: + app: {{ template "couchdb.name" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + clusterIP: None + ports: + - name: couchdb + port: 5984 + selector: + app: {{ template "couchdb.name" . }} + release: {{ .Release.Name }} diff --git a/incubator/couchdb/templates/ingress.yaml b/incubator/couchdb/templates/ingress.yaml new file mode 100644 index 0000000000..e73d7a5688 --- /dev/null +++ b/incubator/couchdb/templates/ingress.yaml @@ -0,0 +1,32 @@ +{{- if .Values.ingress.enabled -}} +{{- $serviceName := include "couchdb.fullname" . -}} +{{- $servicePort := .Values.service.externalPort -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ template "couchdb.fullname" . }} + labels: + app: {{ template "couchdb.name" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + annotations: + {{- range $key, $value := .Values.ingress.annotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} +spec: + rules: + {{- range $host := .Values.ingress.hosts }} + - host: {{ $host }} + http: + paths: + - path: / + backend: + serviceName: {{ $serviceName }} + servicePort: {{ $servicePort }} + {{- end -}} + {{- if .Values.ingress.tls }} + tls: +{{ toYaml .Values.ingress.tls | indent 4 }} + {{- end -}} +{{- end -}} diff --git a/incubator/couchdb/templates/secrets.yaml b/incubator/couchdb/templates/secrets.yaml new file mode 100644 index 0000000000..06513bb025 --- /dev/null +++ b/incubator/couchdb/templates/secrets.yaml @@ -0,0 +1,16 @@ +{{- if .Values.createAdminSecret -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "couchdb.fullname" . }} + labels: + app: {{ template "couchdb.fullname" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +type: Opaque +data: + adminUsername: {{ template "couchdb.defaultsecret" .Values.adminUsername }} + adminPassword: {{ template "couchdb.defaultsecret" .Values.adminPassword }} + cookieAuthSecret: {{ template "couchdb.defaultsecret" .Values.cookieAuthSecret }} +{{- end -}} diff --git a/incubator/couchdb/templates/service.yaml b/incubator/couchdb/templates/service.yaml new file mode 100644 index 0000000000..3393d0447b --- /dev/null +++ b/incubator/couchdb/templates/service.yaml @@ -0,0 +1,20 @@ +{{- if .Values.service.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "couchdb.svcname" . }} + labels: + app: {{ template "couchdb.name" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + ports: + - port: {{ .Values.service.externalPort }} + protocol: TCP + targetPort: 5984 + type: {{ .Values.service.type }} + selector: + app: {{ template "couchdb.name" . }} + release: {{ .Release.Name }} +{{- end -}} diff --git a/incubator/couchdb/templates/statefulset.yaml b/incubator/couchdb/templates/statefulset.yaml new file mode 100644 index 0000000000..be0c4c10df --- /dev/null +++ b/incubator/couchdb/templates/statefulset.yaml @@ -0,0 +1,123 @@ +apiVersion: apps/v1beta2 +kind: StatefulSet +metadata: + name: {{ template "couchdb.fullname" . }} + labels: + app: {{ template "couchdb.name" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.clusterSize }} + serviceName: {{ template "couchdb.fullname" . }} + podManagementPolicy: {{ .Values.podManagementPolicy }} + selector: + matchLabels: + app: {{ template "couchdb.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "couchdb.name" . }} + release: {{ .Release.Name }} + spec: + containers: + - name: couchdb + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: couchdb + containerPort: 5984 + - name: epmd + containerPort: 4369 + - containerPort: 9100 + env: +{{- if not .Values.allowAdminParty }} + - name: COUCHDB_USER + valueFrom: + secretKeyRef: + name: {{ template "couchdb.fullname" . }} + key: adminUsername + - name: COUCHDB_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "couchdb.fullname" . }} + key: adminPassword + - name: COUCHDB_SECRET + valueFrom: + secretKeyRef: + name: {{ template "couchdb.fullname" . }} + key: cookieAuthSecret +{{- end }} + - name: ERL_FLAGS + value: "{{ range $k, $v := .Values.erlangFlags }} -{{ $k }} {{ $v }} {{ end }}" + livenessProbe: + httpGet: + path: / + port: 5984 + readinessProbe: + httpGet: + path: /_up + port: 5984 + resources: +{{ toYaml .Values.resources | indent 12 }} + volumeMounts: + - name: database-storage + mountPath: /opt/couchdb/data + - name: config + mountPath: /opt/couchdb/etc/default.d + - name: couchdb-statefulset-assembler + image: "{{ .Values.helperImage.repository }}:{{ .Values.helperImage.tag }}" + imagePullPolicy: {{ .Values.helperImage.pullPolicy }} +{{- if not .Values.allowAdminParty }} + env: + - name: COUCHDB_USER + valueFrom: + secretKeyRef: + name: {{ template "couchdb.fullname" . }} + key: adminUsername + - name: COUCHDB_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "couchdb.fullname" . }} + key: adminPassword +{{- end }} +{{- if .Values.nodeSelector }} + nodeSelector: +{{ toYaml .Values.nodeSelector | indent 8 }} +{{- end }} + volumes: + - name: config + configMap: + name: {{ template "couchdb.fullname" . }} + items: + - key: inifile + path: chart.ini +{{- if not .Values.persistentVolume.enabled }} + - name: database-storage + emptyDir: {} +{{- else }} + volumeClaimTemplates: + - metadata: + name: database-storage + labels: + app: {{ template "couchdb.name" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + spec: + accessModes: + {{- range .Values.persistentVolume.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistentVolume.size | quote }} + {{- if .Values.persistentVolume.storageClass }} + {{- if (eq "-" .Values.persistentVolume.storageClass) }} + storageClassName: "" + {{- else }} + storageClassName: "{{ .Values.persistentVolume.storageClass }}" + {{- end }} + {{- end }} +{{- end }} diff --git a/incubator/couchdb/values.yaml b/incubator/couchdb/values.yaml new file mode 100644 index 0000000000..ad414332a7 --- /dev/null +++ b/incubator/couchdb/values.yaml @@ -0,0 +1,107 @@ +## clusterSize is the initial size of the CouchDB cluster. +clusterSize: 3 + +## If allowAdminParty is enabled the cluster will start up without any database +## administrator account; i.e., all users will be granted administrative +## access. Otherwise, the system will look for a Secret called +## -couchdb containing `adminUsername`, `adminPassword` and +## `cookieAuthSecret` keys. See the `createAdminSecret` flag. +## ref: https://kubernetes.io/docs/concepts/configuration/secret/ +allowAdminParty: false + +## If createAdminSecret is enabled a Secret called -couchdb will +## be created containing auto-generated credentials. Users who prefer to set +## these values themselves have a couple of options: +## +## 1) The `adminUsername`, `adminPassword`, and `cookieAuthSecret` can be +## defined directly in the chart's values. Note that all of a chart's values +## are currently stored in plaintext in a ConfigMap in the tiller namespace. +## +## 2) This flag can be disabled and a Secret with the required keys can be +## created ahead of time. +createAdminSecret: true + +adminUsername: admin +# adminPassword: this_is_not_secure +# cookieAuthSecret: neither_is_this + +## The storage volume used by each Pod in the StatefulSet. If a +## persistentVolume is not enabled, the Pods will use `emptyDir` ephemeral +## local storage. Setting the storageClass attribute to "-" disables dynamic +## provisioning of Persistent Volumes; leaving it unset will invoke the default +## provisioner. +persistentVolume: + enabled: false + accessModes: + - ReadWriteOnce + size: 10Gi + # storageClass: "-" + +## The CouchDB image +image: + repository: couchdb + tag: 2.1.1 + pullPolicy: IfNotPresent + +## Sidecar that connects the individual Pods into a cluster +helperImage: + repository: kocolosk/couchdb-statefulset-assembler + tag: 1.1.0 + pullPolicy: IfNotPresent + +## CouchDB is happy to spin up cluster nodes in parallel, but if you encounter +## problems you can try setting podManagementPolicy to the StatefulSet default +## `OrderedReady` +podManagementPolicy: Parallel + +## A StatefulSet requires a headless Service to establish the stable network +## identities of the Pods, and that Service is created automatically by this +## chart without any additional configuration. The Service block below refers +## to a second Service that governs how clients connect to the CouchDB cluster. +service: + enabled: true + type: ClusterIP + externalPort: 5984 + +## An Ingress resource can provide name-based virtual hosting and TLS +## termination among other things for CouchDB deployments which are accessed +## from outside the Kubernetes cluster. +## ref: https://kubernetes.io/docs/concepts/services-networking/ingress/ +ingress: + enabled: false + hosts: + - chart-example.local + annotations: + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + tls: + # Secrets must be manually created in the namespace. + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +## Optional resource requests and limits for the CouchDB container +## ref: http://kubernetes.io/docs/user-guide/compute-resources/ +resources: {} + # requests: + # cpu: 100m + # memory: 128Mi + # limits: + # cpu: 56 + # memory: 256Gi + +## erlangFlags is a map that is passed to the Erlang VM as flags using the +## ERL_FLAGS env. `name` and `setcookie` flags are minimally required to +## establish connectivity between cluster nodes. +## ref: http://erlang.org/doc/man/erl.html#init_flags +erlangFlags: + name: couchdb + setcookie: monster + +## couchdbConfig will override default CouchDB configuration settings. +## The contents of this map are reformatted into a .ini file laid down +## by a ConfigMap object. +## ref: http://docs.couchdb.org/en/latest/config/index.html +couchdbConfig: + # cluster: + # q: 8 # Create 8 shards for each database