Merge "Add link for OpenContrail 4.0 -> 4.1" into release/2019.2.0
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/_common.sls b/kubernetes/_common.sls
index ae8a851..92c2856 100644
--- a/kubernetes/_common.sls
+++ b/kubernetes/_common.sls
@@ -36,6 +36,7 @@
 /etc/containerd/config.toml:
   file.managed:
   - source: salt://kubernetes/files/containerd/config.toml
+  - makedirs: True
   - template: jinja
   - user: root
   - group: root
@@ -134,6 +135,12 @@
     - group: root
     - mode: 0750
 
+/var/log/criproxy:
+  file.directory:
+    - user: root
+    - group: root
+    - mode: 0750
+
 /etc/criproxy/node.conf:
   file.managed:
     - user: root
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/systemd/criproxy.service b/kubernetes/files/systemd/criproxy.service
index 9980501..2279313 100644
--- a/kubernetes/files/systemd/criproxy.service
+++ b/kubernetes/files/systemd/criproxy.service
@@ -16,7 +16,7 @@
 [Service]
 SyslogIdentifier=criproxy
 User=root
-ExecStart=/usr/bin/criproxy -alsologtostderr \
+ExecStart=/usr/bin/criproxy -logtostderr \
           {%- if common.get('containerd', {}).get('enabled') %}
           -connect /run/containerd/containerd.sock,virtlet.cloud:/run/virtlet.sock \
           {%- else %}
@@ -24,7 +24,6 @@
           {%- endif %}
           -listen /var/run/criproxy.sock \
           -v 3 \
-          -alsologtostderr \
 {%- if salt['pkg.version_cmp'](version,'1.8') < 0 %}
           -apiVersion {{ version }} \
 {%- endif %}
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 112f2d7..19b744c 100644
--- a/kubernetes/master/controller.sls
+++ b/kubernetes/master/controller.sls
@@ -335,6 +335,7 @@
 
 {%- endif %}
 
+{%- if master.namespace is defined %}
 
 {%- for name,namespace in master.namespace.items() %}
 
@@ -345,7 +346,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 %}
@@ -360,6 +365,8 @@
 
 {%- endfor %}
 
+{%- endif %}
+
 {%- if master.registry.secret is defined %}
 
 {%- for name,registry in master.registry.secret.items() %}
diff --git a/kubernetes/meta/fluentd.yml b/kubernetes/meta/fluentd.yml
index 623fcea..3a88acf 100644
--- a/kubernetes/meta/fluentd.yml
+++ b/kubernetes/meta/fluentd.yml
@@ -1,22 +1,21 @@
-{%- from "kubernetes/map.jinja" import common with context -%}
+{%- from "kubernetes/map.jinja" import common, master, pool with context -%}
 {%- if pillar.get('fluentd', {}).get('agent', {}).get('enabled', False) %}
-{%- from "kubernetes/map.jinja" import pool, master %}
-{%- if pool.get('enabled', False) %}
-{% set network = pool.get('network', {}) %}
-{%- else %}
-{%- if master.get('enabled', False) %}
-{% set network = master.get('network', {}) %}
-{% endif %}
-{% endif %}
+  {%- if pool.get('enabled', False) %}
+    {% set network = pool.get('network', {}) %}
+  {%- else %}
+    {%- if master.get('enabled', False) %}
+      {% set network = master.get('network', {}) %}
+    {% endif %}
+  {% endif %}
 
-{%- set positiondb = pillar.fluentd.agent.dir.positiondb %}
+  {%- set positiondb = pillar.fluentd.agent.dir.positiondb %}
 agent:
   plugin:
     fluent-plugin-kubernetes_metadata_filter:
       deb: ['td-agent-additional-plugins']
   config:
     label:
-      {%- if pillar.docker is defined %}
+  {%- if pillar.docker is defined %}
       docker:
         filter:
           add_drop_tag:
@@ -26,7 +25,7 @@
             record:
               - name: drop_event
                 value: ${ record.fetch('attrs', {}).fetch('io.kubernetes.pod.name', '') }
-      {%- endif %}
+  {%- endif %}
       kubernetes:
         input:
           container:
@@ -36,10 +35,16 @@
             path_key: log_path
             pos_file: {{ positiondb }}/kubernetes.pos
             parser:
+  {%- if pillar.docker is defined %}
               type: json
+  {%- else %}
+              # Containerd CRI log format https://regex101.com/r/BAw3bQ/1
+              type: regexp
+              format: /^(?<time>.+) (?<stream>stdout|stderr)( (?<logtag>.))? (?<Payload>.*)$/
+  {%- endif %}
               time_format: '%Y-%m-%dT%H:%M:%S.%NZ'
               keep_time_key: false
-          {%- if network is defined and network.get('calico', {}).get('enabled', False) %}
+  {%- if network is defined and network.get('calico', {}).get('enabled', False) %}
           bird:
             type: tail
             tag: kubernetes.calico.bird
@@ -76,19 +81,19 @@
               time_key: Timestamp
               keep_time_key: false
               format: '/^(?<Timestamp>[^ ]+ [^ ]+) \[(?<orig_severity_label>[^ ]+)\]\[\d+?\] (?<Payload>.*)$/'
-          {%- endif %}
+  {%- endif %}
         filter:
           add_kubernetes_meta:
             tag: 'temp.kubernetes.container.**'
             type: kubernetes_metadata
             kubernetes_url: https://{{ pool.apiserver.host }}:{{ pool.apiserver.secure_port }}
-            {%- if common.get('cloudprovider', {}).get('enabled') and common.get('cloudprovider', {}).get('provider') == 'openstack' %}
+  {%- if common.get('cloudprovider', {}).get('enabled') and common.get('cloudprovider', {}).get('provider') == 'openstack' %}
             client_cert: /etc/kubernetes/ssl/kubelet-client-fqdn.crt
             client_key: /etc/kubernetes/ssl/kubelet-client-fqdn.key
-            {%- else %}
+  {%- else %}
             client_cert: /etc/kubernetes/ssl/kubelet-client.crt
             client_key: /etc/kubernetes/ssl/kubelet-client.key
-            {%- endif %}
+  {%- endif %}
             ca_file: /etc/kubernetes/ssl/ca-kubernetes.crt
             verify_ssl: True
           enrich_container:
@@ -104,7 +109,7 @@
                 value: 6
               - name: programname
                 value: ${ record['kubernetes']['container_name'] }
-          {%- if network is defined and network.get('calico', {}).get('enabled', False) %}
+  {%- if network is defined and network.get('calico', {}).get('enabled', False) %}
           enrich_bird:
             tag: 'kubernetes.calico.bird'
             type: record_transformer
@@ -140,7 +145,7 @@
                 value: ${ {'DEBUG'=>7,'INFO'=>6,'WARNING'=>4,'ERROR'=>3,'FATAL'=>2,'PANIC'=>1}[record['orig_severity_label']].to_i }
               - name: programname
                 value: calico-felix
-          {%- endif %}
+  {%- endif %}
         match:
           cast_service_tag:
             tag: 'temp.kubernetes.container.**'
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: