Merge pull request #1 from apache/tests

Add e2e tests
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..b6d09ca
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,33 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+<!-- Thanks for filing an issue! Before hitting the button, please answer these questions. It's helpful to search the existing GitHub issues first. It's likely that another user has already reported the issue you're facing, or it's a known issue that we're already aware of
+
+Fill in as much of the template below as you can.  If you leave out information, we can't help you as well.
+
+Be ready for followup questions, and please respond in a timely manner. If we can't reproduce a bug or think a feature already exists, we might close your issue.  If we're wrong, PLEASE feel free to reopen it and explain why.
+-->
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**Version of Helm and Kubernetes**:
+
+
+**What happened**:
+
+
+**What you expected to happen**:
+
+
+**How to reproduce it** (as minimally and precisely as possible):
+
+
+**Anything else we need to know**:
+
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..b30df0d
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,27 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+<!-- Thanks for filing an issue! Before hitting the button, please answer these questions. It's helpful to search the existing GitHub issues first. It's likely that another user has already reported the issue you're facing, or it's a known issue that we're already aware of.
+
+Describe *in detail* the feature/behavior/change you'd like to see.
+
+Be ready for followup questions, and please respond in a timely manner. If we can't reproduce a bug or think a feature already exists, we might close your issue.  If we're wrong, PLEASE feel free to reopen it and explain why.
+-->
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..a1db437
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,36 @@
+<!--
+Thank you for contributing to couchdb-helm. Before you submit this PR we'd like to
+make sure you are aware of the chart technical requirements and best practices:
+
+* https://github.com/helm/charts/blob/master/CONTRIBUTING.md#technical-requirements
+* https://github.com/helm/helm/tree/master/docs/chart_best_practices
+
+For a quick overview across what we will look at reviewing your PR, please read
+our review guidelines:
+
+* https://github.com/helm/charts/blob/master/REVIEW_GUIDELINES.md
+
+Following our best practices right from the start will accelerate the review process and
+help get your PR merged quicker.
+
+When updates to your PR are requested, please add new commits and do not squash the
+history. This will make it easier to identify new changes. The PR will be squashed
+anyways when it is merged. Thanks.
+
+Please make sure you test your changes before you push them.
+-->
+
+#### What this PR does / why we need it:
+
+#### Which issue this PR fixes
+*(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*
+  - fixes #
+
+#### Special notes for your reviewer:
+
+#### Checklist
+[Place an '[x]' (no spaces) in all applicable fields. Please remove unrelated fields.
+- [ ] Chart Version bumped
+- [ ] e2e tests pass
+- [ ] Variables are documented in the README.md
+- [ ] Chart tgz added to /docs and index updated
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..bf58f7e
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,27 @@
+# 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.
+
+SHELL=/bin/bash
+
+.PHONY: lint
+lint:
+	@helm lint couchdb
+
+.PHONY: publish
+publish: lint
+	@helm package couchdb -d docs
+	@helm repo index docs --url https://apache.github.io/couchdb-helm
+
+# Run end to end tests using KinD
+.PHONY: test
+test:
+	./test/e2e-kind.sh
diff --git a/README.md b/README.md
index a0bfc15..3bfca11 100644
--- a/README.md
+++ b/README.md
@@ -1,167 +1,33 @@
-# CouchDB
+# CouchDB Helm Charts
 
-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 repository contains assets related to the CouchDB Helm chart.
 
-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.
+## Layout
 
-## TL;DR
+ * `couchdb`: contains the unbundled Helm chart
+ * `test`: containes scripts to test the chart locally using [Kind][5]
 
-```bash
-$ helm repo add couchdb https://apache.github.io/couchdb-helm
-$ helm install couchdb/couchdb --set allowAdminParty=true
-```
+## Testing
 
-## Prerequisites
+`make test` will run an integration test using [Kind][5]. This stands up a Kubernetes cluster locally and ensures the chart will deploy using the default options and Helm.
 
-- Kubernetes 1.8+ with Beta APIs enabled
+## Releasing
 
-## Installing the Chart
+Chart versions are immutable. On every version change, `make publish` should be
+run to create a new chart bundle and update the repostory metadata.
 
-To install the chart with the release name `my-release`:
-
-Add the CouchDB Helm repository:
-
-```bash
-$ helm repo add couchdb https://apache.github.io/couchdb-helm
-```
-
-
-```bash
-$ helm install --name my-release couchdb/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 couchdb/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.
-
-## Upgrading an existing Release to a new major version
-
-A major chart version change (like v0.2.3 -> v1.0.0) indicates that there is an
-incompatible breaking change needing manual actions.
-
-## Migrating from stable/couchdb
-
-This chart replaces the `stable/couchdb` chart previously hosted by Helm and continues the
-version semantics. You can upgrade directly from `stable/couchdb` to this chart using:
-
-```bash
-$ helm repo add couchdb https://apache.github.io/couchdb-helm
-$ helm upgrade my-release couchdb/couchdb
-```
-
-## Configuration
-
-The following table 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  | chttpd.bind_address=any                |
-| `allowAdminParty`               | If enabled, start cluster without admin account       | false (requires creating a Secret)     |
-| `createAdminSecret`             | If enabled, create an admin account and cookie secret | true                                   |
-| `schedulerName`                 | Name of the k8s scheduler (other than default)        | `nil`                                  |
-| `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
-| `enableSearch`                  | Adds a sidecar for Lucene-powered text search         | false                                  |
-
-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                         |
-| `image.repository`              | couchdb                                |
-| `image.tag`                     | 2.3.1                                  |
-| `image.pullPolicy`              | IfNotPresent                           |
-| `searchImage.repository`        | kocolosk/couchdb-search                |
-| `searchImage.tag`               | 0.1.0                                  |
-| `searchImage.pullPolicy`        | IfNotPresent                           |
-| `initImage.repository`          | busybox                                |
-| `initImage.tag`                 | latest                                 |
-| `initImage.pullPolicy`          | Always                                 |
-| `ingress.enabled`               | false                                  |
-| `ingress.hosts`                 | chart-example.local                    |
-| `ingress.annotations`           |                                        |
-| `ingress.tls`                   |                                        |
-| `persistentVolume.accessModes`  | ReadWriteOnce                          |
-| `persistentVolume.storageClass` | Default for the Kube cluster           |
-| `podManagementPolicy`           | Parallel                               |
-| `affinity`                      |                                        |
-| `resources`                     |                                        |
-| `service.annotations`           |                                        |
-| `service.enabled`               | true                                   |
-| `service.type`                  | ClusterIP                              |
-| `service.externalPort`          | 5984                                   |
-| `dns.clusterDomainSuffix`       | cluster.local                          |
-
-
-## Feedback, Issues, Contributing
+## Feedback / Issues / Contributing
 
 General feedback is welcome at our [user][1] or [developer][2] mailing lists.
 
 Apache CouchDB has a [CONTRIBUTING][3] file with details on how to get started
 with issue reporting or contributing to the upkeep of this project. In short,
-use GitHub Issues, do not report anything on Docker's website.
+use GitHub Issues, do not report anything to the Helm team.
 
-## Non-Apache CouchDB Development Team Contributors
-
-- [@natarajaya](https://github.com/natarajaya)
-- [@satchpx](https://github.com/satchpx)
-- [@spanato](https://github.com/spanato)
-- [@jpds](https://github.com/jpds)
-- [@sebastien-prudhomme](https://github.com/sebastien-prudhomme)
-- [@stepanstipl](https://github.com/sebastien-stepanstipl)
-- [@amatas](https://github.com/amatas)
-- [@Chimney42](https://github.com/Chimney42)
-- [@mattjmcnaughton](https://github.com/mattjmcnaughton)
-- [@mainephd](https://github.com/mainephd)
-- [@AdamDang](https://github.com/AdamDang)
-- [@mrtyler](https://github.com/mrtyler)
+The chart follows the technical guidelines / best practices [maintained][4] by the Helm team.
 
 [1]: http://mail-archives.apache.org/mod_mbox/couchdb-user/
 [2]: http://mail-archives.apache.org/mod_mbox/couchdb-dev/
 [3]: https://github.com/apache/couchdb/blob/master/CONTRIBUTING.md
+[4]: https://github.com/helm/charts/blob/master/REVIEW_GUIDELINES.md
+[5]: https://github.com/kubernetes-sigs/kind
diff --git a/Chart.yaml b/couchdb/Chart.yaml
similarity index 89%
rename from Chart.yaml
rename to couchdb/Chart.yaml
index 1d33948..a4519e4 100644
--- a/Chart.yaml
+++ b/couchdb/Chart.yaml
@@ -15,4 +15,6 @@
 maintainers:
   - name: kocolosk
     email: kocolosk@apache.org
+  - name: willholley
+    email: willholley@apache.org
 icon: http://couchdb.apache.org/CouchDB-visual-identity/logo/CouchDB-couch-symbol.svg
diff --git a/couchdb/README.md b/couchdb/README.md
new file mode 100644
index 0000000..a0bfc15
--- /dev/null
+++ b/couchdb/README.md
@@ -0,0 +1,167 @@
+# 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 repo add couchdb https://apache.github.io/couchdb-helm
+$ helm install couchdb/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`:
+
+Add the CouchDB Helm repository:
+
+```bash
+$ helm repo add couchdb https://apache.github.io/couchdb-helm
+```
+
+
+```bash
+$ helm install --name my-release couchdb/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 couchdb/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.
+
+## Upgrading an existing Release to a new major version
+
+A major chart version change (like v0.2.3 -> v1.0.0) indicates that there is an
+incompatible breaking change needing manual actions.
+
+## Migrating from stable/couchdb
+
+This chart replaces the `stable/couchdb` chart previously hosted by Helm and continues the
+version semantics. You can upgrade directly from `stable/couchdb` to this chart using:
+
+```bash
+$ helm repo add couchdb https://apache.github.io/couchdb-helm
+$ helm upgrade my-release couchdb/couchdb
+```
+
+## Configuration
+
+The following table 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  | chttpd.bind_address=any                |
+| `allowAdminParty`               | If enabled, start cluster without admin account       | false (requires creating a Secret)     |
+| `createAdminSecret`             | If enabled, create an admin account and cookie secret | true                                   |
+| `schedulerName`                 | Name of the k8s scheduler (other than default)        | `nil`                                  |
+| `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
+| `enableSearch`                  | Adds a sidecar for Lucene-powered text search         | false                                  |
+
+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                         |
+| `image.repository`              | couchdb                                |
+| `image.tag`                     | 2.3.1                                  |
+| `image.pullPolicy`              | IfNotPresent                           |
+| `searchImage.repository`        | kocolosk/couchdb-search                |
+| `searchImage.tag`               | 0.1.0                                  |
+| `searchImage.pullPolicy`        | IfNotPresent                           |
+| `initImage.repository`          | busybox                                |
+| `initImage.tag`                 | latest                                 |
+| `initImage.pullPolicy`          | Always                                 |
+| `ingress.enabled`               | false                                  |
+| `ingress.hosts`                 | chart-example.local                    |
+| `ingress.annotations`           |                                        |
+| `ingress.tls`                   |                                        |
+| `persistentVolume.accessModes`  | ReadWriteOnce                          |
+| `persistentVolume.storageClass` | Default for the Kube cluster           |
+| `podManagementPolicy`           | Parallel                               |
+| `affinity`                      |                                        |
+| `resources`                     |                                        |
+| `service.annotations`           |                                        |
+| `service.enabled`               | true                                   |
+| `service.type`                  | ClusterIP                              |
+| `service.externalPort`          | 5984                                   |
+| `dns.clusterDomainSuffix`       | cluster.local                          |
+
+
+## Feedback, Issues, Contributing
+
+General feedback is welcome at our [user][1] or [developer][2] mailing lists.
+
+Apache CouchDB has a [CONTRIBUTING][3] file with details on how to get started
+with issue reporting or contributing to the upkeep of this project. In short,
+use GitHub Issues, do not report anything on Docker's website.
+
+## Non-Apache CouchDB Development Team Contributors
+
+- [@natarajaya](https://github.com/natarajaya)
+- [@satchpx](https://github.com/satchpx)
+- [@spanato](https://github.com/spanato)
+- [@jpds](https://github.com/jpds)
+- [@sebastien-prudhomme](https://github.com/sebastien-prudhomme)
+- [@stepanstipl](https://github.com/sebastien-stepanstipl)
+- [@amatas](https://github.com/amatas)
+- [@Chimney42](https://github.com/Chimney42)
+- [@mattjmcnaughton](https://github.com/mattjmcnaughton)
+- [@mainephd](https://github.com/mainephd)
+- [@AdamDang](https://github.com/AdamDang)
+- [@mrtyler](https://github.com/mrtyler)
+
+[1]: http://mail-archives.apache.org/mod_mbox/couchdb-user/
+[2]: http://mail-archives.apache.org/mod_mbox/couchdb-dev/
+[3]: https://github.com/apache/couchdb/blob/master/CONTRIBUTING.md
diff --git a/templates/NOTES.txt b/couchdb/templates/NOTES.txt
similarity index 100%
rename from templates/NOTES.txt
rename to couchdb/templates/NOTES.txt
diff --git a/templates/_helpers.tpl b/couchdb/templates/_helpers.tpl
similarity index 100%
rename from templates/_helpers.tpl
rename to couchdb/templates/_helpers.tpl
diff --git a/templates/configmap.yaml b/couchdb/templates/configmap.yaml
similarity index 100%
rename from templates/configmap.yaml
rename to couchdb/templates/configmap.yaml
diff --git a/templates/headless.yaml b/couchdb/templates/headless.yaml
similarity index 100%
rename from templates/headless.yaml
rename to couchdb/templates/headless.yaml
diff --git a/templates/ingress.yaml b/couchdb/templates/ingress.yaml
similarity index 100%
rename from templates/ingress.yaml
rename to couchdb/templates/ingress.yaml
diff --git a/templates/secrets.yaml b/couchdb/templates/secrets.yaml
similarity index 100%
rename from templates/secrets.yaml
rename to couchdb/templates/secrets.yaml
diff --git a/templates/service.yaml b/couchdb/templates/service.yaml
similarity index 100%
rename from templates/service.yaml
rename to couchdb/templates/service.yaml
diff --git a/templates/statefulset.yaml b/couchdb/templates/statefulset.yaml
similarity index 100%
rename from templates/statefulset.yaml
rename to couchdb/templates/statefulset.yaml
diff --git a/values.yaml b/couchdb/values.yaml
similarity index 100%
rename from values.yaml
rename to couchdb/values.yaml
diff --git a/docs/couchdb-2.2.0.tgz b/docs/couchdb-2.2.0.tgz
new file mode 100644
index 0000000..5bf2044
--- /dev/null
+++ b/docs/couchdb-2.2.0.tgz
Binary files differ
diff --git a/docs/index.yaml b/docs/index.yaml
new file mode 100644
index 0000000..28b731f
--- /dev/null
+++ b/docs/index.yaml
@@ -0,0 +1,27 @@
+apiVersion: v1
+entries:
+  couchdb:
+  - apiVersion: v1
+    appVersion: 2.3.1
+    created: 2019-10-28T09:24:10.747431Z
+    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.
+    digest: f68e6187c2b65a02fdde9d49ec38e76a68c3d82421e5ea9e599bac87f4193c6a
+    home: https://couchdb.apache.org/
+    icon: http://couchdb.apache.org/CouchDB-visual-identity/logo/CouchDB-couch-symbol.svg
+    keywords:
+    - couchdb
+    - database
+    - nosql
+    maintainers:
+    - email: kocolosk@apache.org
+      name: kocolosk
+    - email: willholley@apache.org
+      name: willholley
+    name: couchdb
+    sources:
+    - https://github.com/apache/couchdb-docker
+    urls:
+    - https://apache.github.io/couchdb-helm/couchdb-2.2.0.tgz
+    version: 2.2.0
+generated: 2019-10-28T09:24:10.746176Z
diff --git a/test/ct.yaml b/test/ct.yaml
new file mode 100644
index 0000000..d40aa57
--- /dev/null
+++ b/test/ct.yaml
@@ -0,0 +1 @@
+helm-extra-args: --timeout 800
diff --git a/test/e2e-kind.sh b/test/e2e-kind.sh
new file mode 100755
index 0000000..4301343
--- /dev/null
+++ b/test/e2e-kind.sh
@@ -0,0 +1,97 @@
+#!/usr/bin/env bash
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+readonly CT_VERSION=v2.3.3
+readonly KIND_VERSION=v0.5.1
+readonly CLUSTER_NAME=chart-testing
+readonly K8S_VERSION=v1.14.3
+
+run_ct_container() {
+    echo 'Running ct container...'
+    docker run --rm --interactive --detach --network host --name ct \
+        --volume "$(pwd)/test/ct.yaml:/etc/ct/ct.yaml" \
+        --volume "$(pwd):/workdir" \
+        --workdir /workdir \
+        "quay.io/helmpack/chart-testing:$CT_VERSION" \
+        cat
+    echo
+}
+
+cleanup() {
+    echo 'Removing ct container...'
+    docker kill ct > /dev/null 2>&1
+
+    kind delete cluster --name "$CLUSTER_NAME" || true
+
+    echo 'Done!'
+}
+
+docker_exec() {
+    docker exec --interactive ct "$@"
+}
+
+create_kind_cluster() {
+    if ! [ -x "$(command -v kind)" ]; then
+        echo 'kind not found. See https://kind.sigs.k8s.io/ for installation instructions.'
+        exit
+    fi
+
+    kind delete cluster --name "$CLUSTER_NAME" || true
+    kind create cluster --name "$CLUSTER_NAME" --config test/kind-config.yaml --image "kindest/node:$K8S_VERSION" --wait 60s
+
+    docker_exec mkdir -p /root/.kube
+
+    echo 'Copying kubeconfig to container...'
+    local kubeconfig
+    kubeconfig="$(kind get kubeconfig-path --name "$CLUSTER_NAME")"
+    docker cp "$kubeconfig" ct:/root/.kube/config
+
+    docker_exec kubectl cluster-info
+    echo
+
+    docker_exec kubectl get nodes
+    echo
+
+    echo 'Cluster ready!'
+    echo
+}
+
+install_tiller() {
+    echo 'Installing Tiller...'
+    docker_exec kubectl --namespace kube-system create sa tiller
+    docker_exec kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller
+    docker_exec helm init --service-account tiller --upgrade --wait
+    echo
+}
+
+install_local-path-provisioner() {
+    # kind doesn't support Dynamic PVC provisioning yet, this is one ways to get it working
+    # https://github.com/rancher/local-path-provisioner
+
+    # Remove default storage class. It will be recreated by local-path-provisioner
+    docker_exec kubectl delete storageclass standard
+
+    echo 'Installing local-path-provisioner...'
+    docker_exec kubectl apply -f test/local-path-provisioner.yaml
+    echo
+}
+
+install_charts() {
+    docker_exec ct lint-and-install --chart-repos couchdb=https://apache.github.io/couchdb-helm --chart-dirs .
+    echo
+}
+
+main() {
+    run_ct_container
+    trap cleanup EXIT
+
+    create_kind_cluster
+    install_local-path-provisioner
+    install_tiller
+    install_charts
+}
+
+main
diff --git a/test/kind-config.yaml b/test/kind-config.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/kind-config.yaml
diff --git a/test/local-path-provisioner.yaml b/test/local-path-provisioner.yaml
new file mode 100644
index 0000000..3eda3a1
--- /dev/null
+++ b/test/local-path-provisioner.yaml
@@ -0,0 +1,108 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+  name: local-path-storage
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: local-path-provisioner-service-account
+  namespace: local-path-storage
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRole
+metadata:
+  name: local-path-provisioner-role
+  namespace: local-path-storage
+rules:
+- apiGroups: [""]
+  resources: ["nodes", "persistentvolumeclaims"]
+  verbs: ["get", "list", "watch"]
+- apiGroups: [""]
+  resources: ["endpoints", "persistentvolumes", "pods"]
+  verbs: ["*"]
+- apiGroups: [""]
+  resources: ["events"]
+  verbs: ["create", "patch"]
+- apiGroups: ["storage.k8s.io"]
+  resources: ["storageclasses"]
+  verbs: ["get", "list", "watch"]
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRoleBinding
+metadata:
+  name: local-path-provisioner-bind
+  namespace: local-path-storage
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: local-path-provisioner-role
+subjects:
+- kind: ServiceAccount
+  name: local-path-provisioner-service-account
+  namespace: local-path-storage
+---
+apiVersion: apps/v1beta2
+kind: Deployment
+metadata:
+  name: local-path-provisioner
+  namespace: local-path-storage
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: local-path-provisioner
+  template:
+    metadata:
+      labels:
+        app: local-path-provisioner
+    spec:
+      serviceAccountName: local-path-provisioner-service-account
+      containers:
+      - name: local-path-provisioner
+        image: rancher/local-path-provisioner:v0.0.11
+        imagePullPolicy: Always
+        command:
+        - local-path-provisioner
+        - --debug
+        - start
+        - --config
+        - /etc/config/config.json
+        volumeMounts:
+        - name: config-volume
+          mountPath: /etc/config/
+        env:
+        - name: POD_NAMESPACE
+          valueFrom:
+            fieldRef:
+              fieldPath: metadata.namespace
+      volumes:
+        - name: config-volume
+          configMap:
+            name: local-path-config
+---
+apiVersion: storage.k8s.io/v1
+kind: StorageClass
+metadata:
+  name: local-path
+  annotations:
+    storageclass.kubernetes.io/is-default-class: "true"
+provisioner: rancher.io/local-path
+volumeBindingMode: WaitForFirstConsumer
+reclaimPolicy: Delete
+---
+kind: ConfigMap
+apiVersion: v1
+metadata:
+  name: local-path-config
+  namespace: local-path-storage
+data:
+  config.json: |-
+        {
+                "nodePathMap":[
+                {
+                        "node":"DEFAULT_PATH_FOR_NON_LISTED_NODES",
+                        "paths":["/opt/local-path-provisioner"]
+                }
+                ]
+        }