fix: add sensible defaults and defensive property references

Additionally deprecate certain duplicated or unnecessary configuration
parameters, such as:

* compute the download URL for helm and kubectl from the configured
version
* remove the tests/pillar/single.sls reference to
helm:client:bind:address
* remove the helm:client:kubectl:tarball_path configuration value,
since that’s an implementation detail of the formula that shouldn’t
matter to consumers

Fixes salt-formulas/salt-formula-helm#2
diff --git a/README.rst b/README.rst
index 1bf7c33..b3e830f 100644
--- a/README.rst
+++ b/README.rst
@@ -10,6 +10,14 @@
 Sample pillars
 ==============
 
+See the [pillar.example](pillar.example) for a documented example pillar file.
+
+Example Configurations
+======================
+
+_The following examples demonstrate configuring the formula for different
+use cases._
+
 Enable formula, install helm client on node and tiller on Kubernetes (assuming
 already configured kubectl config or local cluster endpoint):
 
@@ -25,7 +33,7 @@
 
     helm:
       client:
-        version: 2.6.0  # defaults to 2.4.2 currently
+        version: 2.6.0  # defaults to 2.6.2 currently
         download_hash: sha256=youneedtocalculatehashandputithere
 
 Don't install tiller and use existing one exposed on some well-known address:
@@ -84,12 +92,16 @@
         kubectl:
           install: true  # installs kubectl 1.6.7 by default
           config:
-            cluster:  # directly translated to cluster definition in kubeconfig
+            # directly translated to cluster definition in kubeconfig
+            cluster: 
               server: https://kubernetes.example.com
               certificate-authority-data: base64_of_ca_certificate
-            user:  # same for user
+            cluster_name: kubernetes.example
+            # directly translated to user definition in kubeconfig
+            user:
               username: admin
               password: uberadminpass
+            user_name: admin 
 
 Change kubectl download URL and use it with GKE-based cluster:
 
@@ -102,12 +114,15 @@
           download_url: https://dl.k8s.io/v1.6.7/kubernetes-client-linux-amd64.tar.gz
           download_hash: sha256=calculate_hash_here
           config:
-            cluster:  # directly translated to cluster definition in kubeconfig
+            # directly translated to cluster definition in kubeconfig
+            cluster:
               server: https://3.141.59.265
               certificate-authority-data: base64_of_ca_certificate
+            # directly translated to user definition in kubeconfig
             user:
               auth-provider:
                 name: gcp
+            user_name: gce_user
             gce_service_token: base64_of_json_token_downloaded_from_cloud_console
 
 
diff --git a/helm/client.sls b/helm/client.sls
index e37151f..b913efc 100644
--- a/helm/client.sls
+++ b/helm/client.sls
@@ -1,24 +1,26 @@
 {%- from "helm/map.jinja" import client with context %}
 {%- if client.enabled %}
 
-{%- set helm_tmp = "/tmp/helm-" + client.version %}
-{%- set helm_bin = "/usr/bin/helm-" + client.version %}
+{%- set helm_tmp = "/tmp/helm-v" + client.version %}
+{%- set helm_bin = "/usr/bin/helm-v" + client.version %}
 {%- set kubectl_bin = "/usr/bin/kubectl" %}
 {%- set kube_config = "/srv/helm/kubeconfig.yaml" %}
 
-{%- if client.kubectl.config.gce_service_token %}
-{%- set gce_service_token = "/srv/helm/gce_token.json" %}
-{%- set gce_env_var = "- GOOGLE_APPLICATION_CREDENTIALS: \"{}\"".format(gce_service_token) %}
-{%- set gce_state_arg = "- gce_service_token: \"{}\"".format(gce_service_token) %}
-{%- set gce_require = "- file: \"{}\"".format(gce_service_token) %}
-{%- else %}
+{%- set gce_service_token = None %}
 {%- set gce_env_var = "" %}
 {%- set gce_state_arg = "" %}
 {%- set gce_require = "" %}
+{%- if client.kubectl.install and 
+       "gce_service_token" in client.kubectl.config %}
+{%- set gce_service_token = client.kubectl.config.gce_service_token %}
+{%- set gce_service_token_path = "/srv/helm/gce_token.json" %}
+{%- set gce_env_var = "- GOOGLE_APPLICATION_CREDENTIALS: \"{}\"".format(gce_service_token_path) %}
+{%- set gce_state_arg = "- gce_service_token: \"{}\"".format(gce_service_token_path) %}
+{%- set gce_require = "- file: \"{}\"".format(gce_service_token_path) %}
 {%- endif %}
 
 {%- set helm_home = "/srv/helm/home" %}
-{%- if client.tiller.host %}
+{%- if "host" in client.tiller %}
 {%- set helm_run = "helm --host '{}'".format(client.tiller.host) %}
 {%- set tiller_arg = "- tiller_host: \"{}\"".format(client.tiller.host) %}
 {%- else %}
@@ -31,7 +33,7 @@
     - user: root
     - group: root
   archive.extracted:
-    - source: {{ client.download_url }}
+    - source: https://storage.googleapis.com/kubernetes-helm/helm-v{{ client.version }}-linux-amd64.tar.gz
     - source_hash: {{ client.download_hash }}
     - archive_format: tar
     {%- if grains['saltversioninfo'] < [2016, 11] %}
@@ -54,7 +56,7 @@
 
 /usr/bin/helm:
   file.symlink:
-    - target: helm-{{ client.version }}
+    - target: helm-v{{ client.version }}
     - require:
       - file: {{ helm_bin }}
 
@@ -67,6 +69,7 @@
     - require:
       - file: /usr/bin/helm
 
+{%- if client.kubectl.install %}
 {{ kube_config }}:
   file.managed:
     - source: salt://helm/files/kubeconfig.yaml.j2
@@ -75,8 +78,8 @@
     - group: root
     - template: jinja
 
-{%- if client.kubectl.config.gce_service_token %}
-{{ gce_service_token }}:
+{%- if gce_service_token %}
+{{ gce_service_token_path }}:
   file.managed:
     - source: salt://helm/files/gce_token.json.j2
     - mode: 400
@@ -84,8 +87,33 @@
     - group: root
     - template: jinja
     - context:
-        content: {{ client.kubectl.config.gce_service_token }}
-{%- endif %}
+        content: {{ gce_service_token }}
+{%- endif %}{# gce_service_token #}
+
+extract_kubectl:
+  archive.extracted:
+    - name: {{ helm_tmp }}/kubectl/v{{ client.kubectl.version }}
+    - source: https://dl.k8s.io/v{{ client.kubectl.version }}/kubernetes-client-linux-amd64.tar.gz
+    - source_hash: {{ client.kubectl.download_hash }}
+    - archive_format: tar
+    {%- if grains['saltversioninfo'] < [2016, 11] %}
+    - tar_options: v
+    {%- else %}
+    - options: v
+    {%- endif %}
+    - if_missing: {{ helm_tmp }}/kubectl/v{{ client.kubectl.version }}
+    - require:
+      - file: {{ helm_tmp }}
+
+{{ kubectl_bin }}:
+  file.managed:
+    - source: {{ helm_tmp }}/kubectl/v{{ client.kubectl.version }}/kubernetes/client/bin/kubectl
+    - mode: 555
+    - user: root
+    - group: root
+    - require:
+      - archive: extract_kubectl
+{%- endif %}{# client.kubectl.install #}
 
 helm_env_home_param:
    environ.setenv:
@@ -107,13 +135,17 @@
     - name: {{ helm_run }} init --upgrade
     - env:
       - HELM_HOME: {{ helm_home }}
+      {%- if client.kubectl.install %}
       - KUBECONFIG: {{ kube_config }}
+      {%- endif %}
       {{ gce_env_var }}
     - unless: "{{ helm_run }} version --server --short | grep -E 'Server: v{{ client.version }}(\\+|$)'"
     - require:
       - cmd: prepare_client
+      {%- if client.kubectl.install %}
       - file: {{ kube_config }}
       - environ: helm_env_kubeconfig_param
+      {%- endif %}
       {{ gce_require }}
 
 wait_for_tiller:
@@ -122,15 +154,22 @@
     - timeout: 30
     - env:
       - HELM_HOME: {{ helm_home }}
+      {%- if client.kubectl.install %}
       - KUBECONFIG: {{ kube_config }}
+      {%- endif %}
       {{ gce_env_var }}
+    {%- if client.kubectl.install or gce_require != "" %}
     - require:
+      {%- if client.kubectl.install %}
       - file: {{ kube_config }}
+      {%- endif %}
       {{ gce_require }}
+    {%- endif %}
     - onchanges:
       - cmd: install_tiller
 {%- endif %}
 
+{%- if "repos" in client %}
 {%- for repo_name, repo_url in client.repos.items() %}
 ensure_{{ repo_name }}_repo:
   cmd.run:
@@ -141,7 +180,9 @@
     - require:
       - cmd: prepare_client
 {%- endfor %}
+{%- endif %}{# "repos" in client #}
 
+{%- if "releases" in client %}
 {%- set namespaces = [] %}
 {%- for release_id, release in client.releases.items() %}
 {%- set release_name = release.get('name', release_id) %}
@@ -152,7 +193,9 @@
     - name: {{ release_name }}
     - chart_name: {{ release['chart'] }}
     - namespace: {{ namespace }}
+    {% if client.kubectl.install %}
     - kube_config: {{ kube_config }}
+    {% endif %}
     {{ tiller_arg }}
     {{ gce_state_arg }}
     {%- if release.get('version') %}
@@ -168,13 +211,15 @@
 {%- endif %}
       - cmd: ensure_{{ namespace }}_namespace
       {{ gce_require }}
-    {%- do namespaces.append((namespace, None)) %}
+    {%- do namespaces.append(namespace) %}
 {%- else %}{# not release.enabled #}
 absent_{{ release_id }}_release:
   helm_release.absent:
     - name: {{ release_name }}
     - namespace: {{ namespace }}
+    {% if client.kubectl.install %}
     - kube_config: {{ kube_config }}
+    {% endif %}
     {{ tiller_arg }}
     {{ gce_state_arg }}
     - require:
@@ -185,47 +230,25 @@
       - cmd: prepare_client
 {%- endif %}{# release.enabled #}
 {%- endfor %}{# release_id, release in client.releases #}
+{%- endif %}{# "releases" in client #}
 
-{%- if client.kubectl.install %}
-extract_kubectl:
-  archive.extracted:
-    - name: {{ helm_tmp }}
-    - source: {{ client.kubectl.download_url }}
-    - source_hash: {{ client.kubectl.download_hash }}
-    - archive_format: tar
-    {%- if grains['saltversioninfo'] < [2016, 11] %}
-    - tar_options: v
-    {%- else %}
-    - options: v
-    {%- endif %}
-    - if_missing: {{ helm_tmp }}/{{ client.kubectl.tarball_path }}
-    - require:
-      - file: {{ helm_tmp }}
-
-{{ kubectl_bin }}:
-  file.managed:
-    - source: {{ helm_tmp }}/{{ client.kubectl.tarball_path }}
-    - mode: 555
-    - user: root
-    - group: root
-    - require:
-      - archive: extract_kubectl
-{%- endif %}{# client.kubectl.install #}
-
-{%- for namespace in dict(namespaces) %}
+{%- for namespace in namespaces %}
 ensure_{{ namespace }}_namespace:
   cmd.run:
     - name: kubectl create namespace {{ namespace }}
     - unless: kubectl get namespace {{ namespace }}
     - env:
-      - KUBECONFIG: {{ kube_config }}
       {{ gce_env_var }}
+      {%- if client.kubectl.install %}
+      - KUBECONFIG: {{ kube_config }}
+      {%- endif %}
+    {%- if gce_require != "" or client.kubectl.install %}
     - require:
-      - file: {{ kube_config }}
-      - environ: helm_env_kubeconfig_param
       {{ gce_require }}
-    {%- if client.kubectl.install %}
+      {%- if client.kubectl.install %}
+      - file: {{ kube_config }}
       - file: {{ kubectl_bin }}
+      {%- endif %}
     {%- endif %}
 {%- endfor %}
 
diff --git a/helm/files/kubeconfig.yaml.j2 b/helm/files/kubeconfig.yaml.j2
index 753362b..1e330ad 100644
--- a/helm/files/kubeconfig.yaml.j2
+++ b/helm/files/kubeconfig.yaml.j2
@@ -1,19 +1,40 @@
 {%- from "helm/map.jinja" import client with context %}
 {%- set config = client.kubectl.config %}
+{%- set cluster = config.get("cluster", None) %}
+{%- set cluster_name = config.get("cluster_name", "thecluster") %}
+{%- set user_name = config.get("user_name", "theuser") %}
+{%- set context_name = config.get('context_name', "\"\"") %}
+{%- set context = config.get("context", None) %}
+{%- set user = config.get("user", None) %}
 apiVersion: v1
-clusters:
-- cluster:
-    {{ config.cluster|yaml|indent(4) }}
-  name: thecluster
-contexts:
-- context:
-    cluster: thecluster
-    user: theuser
-  name: thecontext
-current-context: thecontext
+
+{%- if cluster is not none %}
+clusters: 
+  - name: {{ cluster_name }}
+    cluster: 
+      {{ cluster | yaml(False) |indent(6) }}
+{%- else %}
+clusters: []
+{%- endif %}
+
+{%- if context is not none %}
+contexts: 
+  - name: {{ context_name }}
+    context: 
+      cluster: {{ cluster_name }}
+      user: {{ user_name }}
+{%- else %}
+contexts: []
+{%- endif %}
+
+current-context: {{ context_name }}
 kind: Config
 preferences: {}
-users:
-- name: theuser
-  user:
-    {{ config.user|yaml|indent(4) }}
+
+{%- if user is not none %}
+users: 
+  - name: {{ user_name }}
+    {{ config.get("user", "") | yaml(false) | indent(4) }}
+{%- else %}
+users: []
+{%- endif %}
\ No newline at end of file
diff --git a/helm/map.jinja b/helm/map.jinja
index 5aa27e2..5eef827 100644
--- a/helm/map.jinja
+++ b/helm/map.jinja
@@ -26,4 +26,21 @@
 {%- endif %}
 {%- endload %}
 
-{%- set client = salt['grains.filter_by'](base_defaults, merge=salt['pillar.get']('helm:client')) %}
\ No newline at end of file
+{%- load_yaml as base_config %}
+helm:
+  client:
+    enabled: true
+    version: 2.6.2
+    download_hash: sha256=ba807d6017b612a0c63c093a954c7d63918d3e324bdba335d67b7948439dbca8
+    tiller:
+      install: true
+      namespace: kube-system
+    kubectl:
+      install: true
+      version: 1.6.7
+      download_hash: sha256=54947ef84181e89f9dbacedd54717cbed5cc7f9c36cb37bc8afc9097648e2c91
+      config: {}
+{%- endload %}
+
+{%- set config = salt['pillar.get']('helm:client', base_config.helm.client, merge=true) %}
+{%- set client = salt['grains.filter_by'](base_defaults, merge=config) %}
\ No newline at end of file
diff --git a/metadata/service/client.yml b/metadata/service/client.yml
index ccd42f7..b46a69a 100644
--- a/metadata/service/client.yml
+++ b/metadata/service/client.yml
@@ -7,12 +7,10 @@
     client:
       enabled: true
       version: 2.4.2
-      download_url: https://storage.googleapis.com/kubernetes-helm/helm-v${helm:client:version}-linux-amd64.tar.gz
       download_hash: sha256=96f74ff04ec7eb38e5f53aba73132bfe4d6b81168f20574dad25a9bcaceec81b
       tiller:
         install: true
         namespace: kube-system
-        host:
       kubectl:
         install: false
         download_url: https://dl.k8s.io/v1.6.7/kubernetes-client-linux-amd64.tar.gz
diff --git a/pillar.example b/pillar.example
new file mode 100644
index 0000000..156b411
--- /dev/null
+++ b/pillar.example
@@ -0,0 +1,163 @@
+helm:
+  client:
+
+    #
+    # The version of the Helm client to install
+    #
+    # version: 2.6.2
+    
+    #
+    # The hash for the helm client binary. You must calculate the hash for the
+    # version of the binary you install. 
+    # Defaults to the SHA 256 hash for the helm-v2.6.2-linux-amd64.tar.gz
+    # 
+    # 
+    # The binary is downloaded from:
+    # 
+    # https://storage.googleapis.com/kubernetes-helm/helm-v[[ client.version ]]-linux-amd64.tar.gz
+    # 
+    # Here is an example command you can use to calculate the sha256 hash for 
+    # the binary:
+    # 
+    # ```
+    # shasum -a 256 /path/to/helm-v[[ client.version ]]-linux.amd64.tar.gz
+    # ```
+    # 
+    # download_hash: sha256=ba807d6017b612a0c63c093a954c7d63918d3e324bdba335d67b7948439dbca8
+    
+    #
+    # Whether the helm client should be enabled for the target minion or not
+    # 
+    # TODO: this should be removed.
+    #
+    enabled: true
+
+    #
+    # Configurations to manage the cluster's Tiller installation
+    #
+    # tiller:
+      #
+      # Whether Tiller should be deployed to the kubernetes cluster as part of
+      # this formaul. Defaults to true.
+      #
+      # install: true
+
+      #
+      # The namespace to which Tiller should be installed (only used if 
+      # `helm:client:tiller:install` is set to true).
+      # Defaults to `kube-system`
+      #
+      # naamespace: kube-system
+
+      #
+      # The host IP or name and port for an existing tiller installation that
+      # should be used by the Helm client. Defaults to Helm's default if
+      # unspecified.
+      #
+      # host:
+
+    #
+    # Configurations defined to manage the target minion's kubectl installation
+    #
+    # kubectl:
+      #
+      # Whether kubectl should be installed as part of this formula. 
+      # Defaults to false
+      #
+      # install: false
+      
+      #
+      # The version of the kubectl binary to install.
+      # Defaults to 1.6.7
+      #
+      # version: 1.6.7
+
+      #
+      # The hash for the kubectl binary version to install. You must calculate 
+      # the hash for the version of the binary you install. 
+      # 
+      # 
+      # The binary is downloaded from:
+      # 
+      # https://dl.k8s.io/v[[ client.kubectl.version ]]/kubernetes-client-linux-amd64.tar.gz
+      # 
+      # 
+      # Defaults to the SHA 256 hash for the Linux distribution of version 1.6.7
+      # 
+      # Here is an example command you can use to calculate the sha256 hash for 
+      # the binary:
+      # 
+      # ```
+      # shasum -a 256 /path/to/kubernetes-client-linux-amd64.tar.gz
+      # ```
+      # 
+      # download_hash: sha256=54947ef84181e89f9dbacedd54717cbed5cc7f9c36cb37bc8afc9097648e2c91
+      
+      
+      #
+      # Configuration parameters that should be applied to the kubectl 
+      # installation's kubeconfig. Not that this will only be applied to the 
+      # kubectl installation managed by this formula.
+      # 
+      # While the kubectl tool can be configured to connect to multiple 
+      # clusters and allow switching between cluster contexts, this kubectl
+      # configuration managed by this formula will only be configured with
+      # the cluster context details used by this formula.
+      #
+      # config:
+      #   cluster: 
+      #     server: https://kubernetes.example.com
+      #     certificate-authority-data: base64_of_ca_certificate
+      #   cluster_name: kubernetes.example
+      #   context_name: kubernetes-example
+      #   user:
+      #     username: admin
+      #     password: uberadminpass
+      #   user_name: admin
+      
+
+    #
+    # The mapping of repository names to urls that should be registered and
+    # kept up-to-date with the helm client
+    #
+    repos:
+      mirantisworkloads: https://mirantisworkloads.storage.googleapis.com/
+      incubator: https://kubernetes-charts-incubator.storage.googleapis.com/
+
+    #
+    # The listing of releases that should be managed by the formula. Note that
+    # if configured, the releases listed under this `helm:client:releases` key
+    # will be used as an authoritative, exclusive listing of the releases that
+    # should be configured and deployed to the Tiller installation; any 
+    # release existing in the tiller cluster that is not configured here 
+    # **will be deleted**
+    #
+    releases:
+      zoo1:
+
+        #
+        # The name of the release
+        #
+        name: my-zookeeper
+
+        #
+        # The repository name and chart name combination for the chart to
+        # release
+        #
+        chart: mirantisworkloads/zookeeper
+
+        #
+        # The version of the helm chart to install
+        #
+        version: 1.2.0 
+
+        #
+        # The namespace to which the release should be deployed
+        #
+        namespace: helm-example-namespace
+
+        # 
+        # Configuration values that should be supplied to the chart.
+        #
+        values:
+          logLevel: INFO 
\ No newline at end of file
diff --git a/tests/pillar/single.sls b/tests/pillar/single.sls
index e8f824c..d2ae52c 100644
--- a/tests/pillar/single.sls
+++ b/tests/pillar/single.sls
@@ -2,18 +2,11 @@
   client:
     enabled: true
     version: 2.6.0
-    download_url: https://storage.googleapis.com/kubernetes-helm/helm-v2.6.0-linux-amd64.tar.gz
     download_hash: sha256=506e477a9eb61730a2fb1af035357d35f9581a4ffbc093b59e2c2af7ea3beb41
-    bind:
-      address: 0.0.0.0
     tiller:
       install: false
       host: 10.11.12.13:14151
     kubectl:
-      install: true  # installs kubectl 1.6.7 by default
-      download_url: https://dl.k8s.io/v1.6.7/kubernetes-client-linux-amd64.tar.gz
-      download_hash: sha256=54947ef84181e89f9dbacedd54717cbed5cc7f9c36cb37bc8afc9097648e2c91
-      tarball_path: kubernetes/client/bin/kubectl
       config:
         cluster:  # directly translated to cluster definition in kubeconfig
           server: https://kubernetes.example.com