Merge "Drop testing of SaltStack 2016.3"
diff --git a/README.rst b/README.rst
index 263d1a3..3cf026d 100644
--- a/README.rst
+++ b/README.rst
@@ -1185,6 +1185,29 @@
                   test:
                     kind: User
 
+Manage client (kubectl resources)
+Assign labels to nodes:
+
+.. code-block:: yaml
+
+    kubernetes:
+      client:
+        enabled: true
+        apiserver:
+          insecure_address: 0.0.0.0
+          insecure_port: 8080
+        resources:
+          enabled: true
+          label:
+            test:
+              value: enabled
+              status: 'present'
+              node:
+               - cmp1
+               - cmp2
+              enabled: true
+              key: mylabel
+
 More Information
 ================
 
diff --git a/kubernetes/client.sls b/kubernetes/client.sls
new file mode 100644
index 0000000..9c1c383
--- /dev/null
+++ b/kubernetes/client.sls
@@ -0,0 +1,33 @@
+{%- from "kubernetes/map.jinja" import client with context -%}
+{%- if client.enabled %}
+  {%- if client.get('resources', {}).get('enabled') %}
+
+    {%- for name,label in client.resources.get('label', {}).iteritems() %}
+
+      {%- if label.enabled %}
+        {%- if label.get('status', 'present') == 'present' %}
+          {%- for node in label.node %}
+# TODO(vsaienko) switch to kubernetes. salt module once kubernets-client python is packages and
+# awailable for installation.
+{{ name }}_{{ node }}:
+  k8s.label_present:
+    - name: {{ label.key }}
+    - value: {{ label.value }}
+    - node: {{ node }}
+    # TODO(vsaienko): move to profiles
+    - apiserver: http://{{ client.apiserver.insecure_address }}:{{ client.apiserver.insecure_port }}
+          {%- endfor %}
+
+        {%- elif label.get('status', 'present') == 'absent' %}
+          {%- for node in label.node %}
+{{ name }}_{{ node }}:
+  k8s.label_absent:
+    - name: {{ label.key }}
+    - node: {{ node }}
+    - apiserver: http://{{ client.apiserver.insecure_address }}:{{ client.apiserver.insecure_port }}
+          {%- endfor %} # endfor label.node.iteritems
+        {%- endif %} # endif label.present
+      {%- endif %} # endif label.enabled
+    {%- endfor %} # endfor client.resources.label
+  {%- endif %} # endif client.resources.enabled
+{%- endif %} # endif client.enabled
diff --git a/kubernetes/files/kube-addons/ingress-nginx/ingress-nginx.yaml b/kubernetes/files/kube-addons/ingress-nginx/ingress-nginx.yaml
index cfd5251..7e28da8 100644
--- a/kubernetes/files/kube-addons/ingress-nginx/ingress-nginx.yaml
+++ b/kubernetes/files/kube-addons/ingress-nginx/ingress-nginx.yaml
@@ -376,7 +376,7 @@
     app.kubernetes.io/part-of: ingress-nginx
     addonmanager.kubernetes.io/mode: Reconcile
 spec:
-  externalTrafficPolicy: Local
+  externalTrafficPolicy: {{ common.addons.get('ingress-nginx', {}).get('externalTrafficPolicy', 'Local') }}
   type: LoadBalancer
   selector:
     app.kubernetes.io/name: ingress-nginx
diff --git a/kubernetes/files/rc.yml b/kubernetes/files/rc.yml
index d793510..8edb271 100644
--- a/kubernetes/files/rc.yml
+++ b/kubernetes/files/rc.yml
@@ -107,6 +107,16 @@
       {%- if service.host_pid is defined %}
       hostPID: True
       {%- endif %}
+      {%- if service.host_aliases is defined %}
+      hostAliases:
+        {%- for host_alias in service.host_aliases %}
+        - ip: {{ host_alias.ip }}
+          hostnames:
+          {%- for hostname in host_alias.hostnames %}
+            - {{ hostname }}
+          {%- endfor %}
+        {%- endfor %}
+      {%- endif %}
       containers:
       {%- for container_name, container in service.container.items() %}
         - name: {{ container_name }}
diff --git a/kubernetes/init.sls b/kubernetes/init.sls
index 22aad26..a882a97 100644
--- a/kubernetes/init.sls
+++ b/kubernetes/init.sls
@@ -10,4 +10,7 @@
 {%- if pillar.kubernetes.control is defined %}
 - kubernetes.control
 {%- endif %}
+{%- if pillar.kubernetes.client is defined %}
+- kubernetes.client
+{%- endif %}
 {%- endif %}
diff --git a/kubernetes/map.jinja b/kubernetes/map.jinja
index 615404c..3c3d4cb 100644
--- a/kubernetes/map.jinja
+++ b/kubernetes/map.jinja
@@ -111,6 +111,9 @@
     },
 }, merge=salt['pillar.get']('kubernetes:control')) %}
 
+{% set client = salt['grains.filter_by']({
+}, merge=salt['pillar.get']('kubernetes:client')) %}
+
 {%- set monitoring = salt['grains.filter_by']({
   'default': {
     'instance_minor_threshold_percent': 0.3,
diff --git a/kubernetes/master/controller.sls b/kubernetes/master/controller.sls
index ce31e34..0bfe698 100644
--- a/kubernetes/master/controller.sls
+++ b/kubernetes/master/controller.sls
@@ -334,6 +334,7 @@
 
 {%- endif %}
 
+{%- if master.namespace is defined %}
 
 {%- for name,namespace in master.namespace.items() %}
 
@@ -344,7 +345,11 @@
 kubernetes_namespace_create_{{ name }}:
   cmd.run:
     - name: kubectl create ns "{{ name }}"
-    - name: kubectl get ns -o=custom-columns=NAME:.metadata.name | grep -v NAME | grep "{{ name }}" > /dev/null || kubectl create ns "{{ name }}"
+    - unless: kubectl get ns -o=custom-columns=NAME:.metadata.name | grep -v NAME | grep "{{ name }}"
+    - retry:
+        attempts: 3
+        until: True
+        interval: 10
     {%- if grains.get('noservices') %}
     - onlyif: /bin/false
     {%- endif %}
@@ -353,12 +358,19 @@
 
 kubernetes_namespace_delete_{{ name }}:
   cmd.run:
-    - name: kubectl get ns -o=custom-columns=NAME:.metadata.name | grep -v NAME | grep "{{ name }}" > /dev/null && kubectl delete ns "{{ name }} || true"
+    - name: kubectl delete ns "{{ name }}"
+    - onlyif:
+      - kubectl get ns -o=custom-columns=NAME:.metadata.name | grep -v NAME | grep "{{ name }}" > /dev/null
+      {%- if grains.get('noservices') %}
+      - /bin/false
+      {%- endif %}
 
 {%- endif %}
 
 {%- endfor %}
 
+{%- endif %}
+
 {%- if master.registry.secret is defined %}
 
 {%- for name,registry in master.registry.secret.items() %}
diff --git a/metadata/service/master/cluster.yml b/metadata/service/master/cluster.yml
index afeb317..c611cda 100644
--- a/metadata/service/master/cluster.yml
+++ b/metadata/service/master/cluster.yml
@@ -47,9 +47,6 @@
       ca: kubernetes
       storage:
         engine: none
-      namespace:
-        kube-system:
-          enabled: True
       network:
         calico:
           etcd:
diff --git a/metadata/service/master/single.yml b/metadata/service/master/single.yml
index be5d701..a9314b1 100644
--- a/metadata/service/master/single.yml
+++ b/metadata/service/master/single.yml
@@ -65,9 +65,6 @@
       ca: kubernetes
       storage:
         engine: none
-      namespace:
-        kube-system:
-          enabled: True
       network:
         calico:
           etcd: