Merge "Extend interfaces file of ovs bridge/port"
diff --git a/.gitreview b/.gitreview
new file mode 100644
index 0000000..443a01c
--- /dev/null
+++ b/.gitreview
@@ -0,0 +1,3 @@
+[gerrit]
+host=gerrit.mcp.mirantis.com
+project=salt-formulas/linux.git
diff --git a/.kitchen.yml b/.kitchen.yml
index 9901242..eeaf317 100644
--- a/.kitchen.yml
+++ b/.kitchen.yml
@@ -33,16 +33,10 @@
   sudo: true
 
 docker_images:
-  - &xenial-20163 <%=ENV['IMAGE_XENIAL_20163'] || 'docker-dev-local.docker.mirantis.net/epcim/salt/saltstack-ubuntu-xenial-salt-2016.3/salt:2018_11_19'%>
   - &xenial-20177 <%=ENV['IMAGE_XENIAL_20177'] || 'docker-dev-local.docker.mirantis.net/epcim/salt/saltstack-ubuntu-xenial-salt-2017.7/salt:2018_11_19'%>
   - &xenial-stable <%=ENV['IMAGE_XENIAL_STABLE'] || 'docker-dev-local.docker.mirantis.net/epcim/salt/saltstack-ubuntu-xenial-salt-stable/salt:2018_11_19'%>
 
 platforms:
-  - name: xenial-2016.3
-    driver_config:
-      image: *xenial-20163
-      platform: ubuntu
-
   - name: xenial-2017.7
     driver_config:
       image: *xenial-20177
diff --git a/README.rst b/README.rst
index dad1aaa..f8ad216 100644
--- a/README.rst
+++ b/README.rst
@@ -76,6 +76,50 @@
             home: '/home/elizabeth'
             password: "$6$nUI7QEz3$dFYjzQqK5cJ6HQ38KqG4gTWA9eJu3aKx6TRVDFh6BVJxJgFWg2akfAA7f1fCxcSUeOJ2arCO6EEI6XXnHXxG10"
 
+Setting user defaults
+---------------------
+Default parameters that will be used by `useradd` command could be configured
+the following way:
+
+.. code-block:: yaml
+
+  linux:
+    system:
+      ...
+      defaults:
+        user:
+          shell: <SHELL>
+          gid: <GROUP>
+          home: <HOME>
+          inactdays: <INACTIVE>
+          expire: <EXPIRE>
+          skeleton: <SKEL>
+          create_mail_spool: <CREATE_MAIL_SPOOL>
+
+Other parameters that are used when creating user profile could be configured
+as well, acting as global defaults:
+
+.. code-block:: yaml
+
+  linux:
+    system:
+      ...
+      defaults:
+        user:
+          ...
+          maxdays: <PASS_MAX_DAYS>
+          mindays: <PASS_MIN_DAYS>
+          warndays: <PASS_WARN_AGE>
+
+.. note::
+
+  The three options above ('maxdays', 'mindays', 'warndays') could be
+  overriden in linux:system:login_defs using their 'real' names.
+  The reason they could be defined here is that it's quite logical to
+  have these parameters related to configuration of user account
+  behaviour in one place.
+
+
 Configure password expiration parameters
 ----------------------------------------
 The following login.defs parameters can be overridden per-user:
@@ -83,7 +127,6 @@
 * PASS_MAX_DAYS
 * PASS_MIN_DAYS
 * PASS_WARN_DAYS
-* INACTIVE
 
 .. code-block:: yaml
 
@@ -97,8 +140,7 @@
             ...
             maxdays: <PASS_MAX_DAYS>
             mindays: <PASS_MIN_DAYS>
-            warndays: <PASS_WARN_DAYS>
-            inactdays: <INACTIVE>
+            warndays: <PASS_WARN_AGE>
 
 Configure sudo for users and groups under ``/etc/sudoers.d/``.
 This ways ``linux.system.sudo`` pillar map to actual sudo attributes:
@@ -519,6 +561,19 @@
               foo: 1
               bar: 'bar'
 
+Ensure presence of file to be decoded through file.decode module (see:
+https://docs.saltstack.com/en/latest/ref/states/all/salt.states.file.html#salt.states.file.decode):
+
+.. code-block:: yaml
+
+    linux:
+      system:
+        file:
+          /tmp/test4.txt:
+            decode: True
+            encoded_data: |
+              dGVzdDQK
+
 Kernel
 ~~~~~~
 
diff --git a/_states/aptkey.py b/_states/aptkey.py
new file mode 100644
index 0000000..8d12829
--- /dev/null
+++ b/_states/aptkey.py
@@ -0,0 +1,121 @@
+# -*- coding: utf-8 -*-
+# _state/aptkey.py
+"""
+Manage apt keys
+"""
+from __future__ import absolute_import, print_function, unicode_literals
+
+from base64 import b64encode, b64decode
+from binascii import Error as binasciiError
+
+def __virtual__():
+    """Only load if 'pkg' (aptpkg) module has necessary functions"""
+
+    if not 'pkg.add_repo_key' in __salt__ or not 'pkg.get_repo_keys' in __salt__:
+        return False, "'pkg.add_repo_key' and 'pkg.get_repo_keys' functions are required"
+    return True
+
+def _get_fingerprints(input_text):
+    """Get fingerprint(s) for given text
+
+    :param str input_text: text to get fingerprint(s) from it
+
+    :return:
+        A list of found fingerprint(s)
+    :rtype list:
+
+    """
+    fingerprints = []
+    cmd = 'apt-key adv --with-fingerprint --with-colons --dry-run -'
+    out = __salt__['cmd.run_stdout'](cmd, stdin=input_text, python_shell=False)
+    for line in out.split('\n'):
+        line = line.split(':')
+        if line[0] == 'fpr':
+            fingerprints.append(line[-2])
+    return fingerprints
+
+
+def added(name, key_text=None, key_url=None):
+    """Ensure that given key added to APT's key storage
+
+    :param str name:
+        Just an ID, it does not used at all.
+    :param str key_text:
+        Key to add as a plain text or base64-encoded.
+    :param str key_url:
+        URL from which key should be fetched. Supported URLs: salt://, http://,
+        https:// and file://.
+
+    Examples:
+
+    .. code-block:: yaml
+
+        # Base64 encoded key
+        {% set repo_key = salt['hashutil.base64_b64encode'](repo.key) %}
+        linux_repo_ubuntu_key:
+          aptkey.added:
+            - key_text: {{ repo_key }}
+
+    .. code-block:: yaml
+
+        # Plaintext key
+        linux_repo_ubuntu_key:
+          aptkey.added:
+            - key_text: '{{ repo.key | replace("\n", "\\n") }}'
+
+    .. code-block:: yaml
+
+        linux_repo_ubuntu_key:
+          aptkey.added:
+            - key_url: 'https://example.com/key.asc'
+    """
+    ret = {'name': name,
+           'result': None if __opts__['test'] else True,
+           'changes': {},
+           'comment': ''}
+
+    if not key_text and not key_url:
+        ret['result'] = False,
+        ret['comment'] = 'No key to add provided'
+        return ret
+    if key_text and key_url:
+        ret['result'] = False
+        ret['comment'] = 'Only one of key_text or key_url is permitted'
+        return ret
+
+    # If key_url provided fetch it before proceeding
+    if key_url:
+        # This only supports salt://, http://, https:// and file:// URLs
+        key_text = __salt__['cp.get_url'](key_url, dest=None)
+
+    # Try to apply base64 decoding to key_text, just in case...
+    try:
+        # Decode key_text if it is base64 encoded string
+        decoded_key_text = b64decode(key_text)
+        # the simplest available check that given string was base64 encoded
+        if b64encode(decoded_key_text) == key_text:
+            key_text = decoded_key_text
+    except TypeError, binasciiError: # the first is for py2, the second for py3
+        pass
+
+    # Get apt's keys and their fingerprints
+    apt_keys = __salt__['pkg.get_repo_keys']()
+    apt_fingerprints = [key.get('fingerprint') for _, key in apt_keys.items()]
+
+    key_fingerprints = _get_fingerprints(key_text)
+    # If any of given fingerprints does not present in apt keys
+    # then add all of them
+    if not set(key_fingerprints).issubset(set(apt_fingerprints)):
+        if __opts__['test']:
+            ret['result'] = None
+            ret['changes'] = {'key': key_text}
+            return ret
+
+        success = __salt__['pkg.add_repo_key'](text=key_text)
+        if success:
+            ret['result'] = True
+            ret['changes'] = {'key': key_text}
+        else:
+            ret['result'] = False
+            ret['comment'] = 'Key was not added because of errors'
+    return ret
diff --git a/linux/files/etc_default_useradd.jinja b/linux/files/etc_default_useradd.jinja
new file mode 100644
index 0000000..076cfeb
--- /dev/null
+++ b/linux/files/etc_default_useradd.jinja
@@ -0,0 +1,50 @@
+# This file is managed by Salt, do not edit
+{%- macro define_option(option_name, option_key, default=None) %}
+  {%- set value = defaults.get(option_key, default) %}
+  {%- if value != None %}
+{{ option_name }}={{ value }}
+  {%- endif %}
+{%- endmacro %}
+# Default values for useradd(8)
+#
+# The SHELL variable specifies the default login shell on your
+# system.
+# Similar to DHSELL in adduser. However, we use "sh" here because
+# useradd is a low level utility and should be as general
+# as possible
+# SHELL=/bin/sh
+{{- define_option('SHELL', 'shell', default='/bin/sh') }}
+#
+# The default group for users
+# 100=users on Debian systems
+# Same as USERS_GID in adduser
+# This argument is used when the -n flag is specified.
+# The default behavior (when -n and -g are not specified) is to create a
+# primary user group with the same name as the user being added to the
+# system.
+# GROUP=100
+{{- define_option('GROUP', 'gid') }}
+#
+# The default home directory. Same as DHOME for adduser
+# HOME=/home
+{{- define_option('HOME', 'home') }}
+#
+# The number of days after a password expires until the account
+# is permanently disabled
+# INACTIVE=-1
+{{- define_option('INACTIVE', 'inactdays') }}
+#
+# The default expire date
+# EXPIRE=
+{{- define_option('EXPIRE', 'expire') }}
+#
+# The SKEL variable specifies the directory containing "skeletal" user
+# files; in other words, files such as a sample .profile that will be
+# copied to the new user's home directory when it is created.
+# SKEL=/etc/skel
+{{- define_option('SKEL', 'skeleton') }}
+#
+# Defines whether the mail spool should be created while
+# creating the account
+# CREATE_MAIL_SPOOL=yes
+{{- define_option('CREATE_MAIN_SPOOL', 'create_mail_spool')}}
\ No newline at end of file
diff --git a/linux/files/login.defs.jinja b/linux/files/login.defs.jinja
index 572c558..ad4d6d3 100644
--- a/linux/files/login.defs.jinja
+++ b/linux/files/login.defs.jinja
@@ -52,11 +52,30 @@
     'USERDEL_CMD',
     'USERGROUPS_ENAB'
 ] %}
+{#
+  'defaults' could be passed externally, in this case it should be dictionary
+#}
+{%- if defaults is not defined %}
+  {%- set defaults = {} %}
+{%- endif %}
+{#
+  Override values in 'defaults' with those defined in system:login_defs as they
+  are of highest priority.
+#}
 {%- for opt_name in allowed_options %}
   {%- if opt_name in login_defs %}
     {%- set opt_params = login_defs.get(opt_name) %}
     {%- if opt_params.get('enabled', true) %}
-{{ opt_name.ljust(20) }} {{ opt_params.value }}
+      {%- do defaults.update({opt_name: opt_params.value}) %}
     {%- endif %}
   {%- endif %}
 {%- endfor %}
+{#
+  Now we can write merged data to file, keeping order of items as in the list above
+#}
+{%- for opt_name in allowed_options %}
+  {%- set value = defaults.get(opt_name, None) %}
+  {%- if value != None %}
+{{ opt_name.ljust(20) }} {{ value }}
+  {%- endif %}
+{%- endfor %}
diff --git a/linux/map.jinja b/linux/map.jinja
index b24e9ce..a485c76 100644
--- a/linux/map.jinja
+++ b/linux/map.jinja
@@ -448,10 +448,6 @@
         'failed_auths_threshold': {
             'warn': 5,
         },
-        'netdev_budget_squeeze_rate': 0.1,
-        'packets_dropped_per_cpu_threshold': {
-            'minor': '0',
-            'major': '100'
-        }
+        'netdev_budget_squeeze_rate': 0.1
     },
 }, grain='os_family', merge=salt['pillar.get']('linux:monitoring')) %}
diff --git a/linux/meta/prometheus.yml b/linux/meta/prometheus.yml
index f405367..753a587 100644
--- a/linux/meta/prometheus.yml
+++ b/linux/meta/prometheus.yml
@@ -198,28 +198,24 @@
       annotations:
         summary: "{{ threshold }}{%- raw %} failed SSH logins"
         description: "{{ $value }} failed SSH login attempts on the {{ $labels.host }} node during the last 5 minutes."
-    PacketsDroppedByCpuMinor:
-      {%- endraw %}
-      {%- set packets_dropped_minor_threshold = monitoring.packets_dropped_per_cpu_threshold.minor %}
+    PacketsDroppedByCpuWarning:
       if: >-
-        floor(increase(nstat_packet_drop[24h])) > {{ packets_dropped_minor_threshold }}
+        floor(increase(nstat_packet_drop[10m])) > 0
+      labels:
+        severity: warning
+        service: system
+      annotations:
+        summary: "Increased number of CPU dropped packets"
+        description: "The {{ $labels.cpu }} CPU on the {{ $labels.host }} node dropped {{ $value }} packets during the last 10 minutes."
+    PacketsDroppedByCpuMinor:
+      if: >-
+        floor(increase(nstat_packet_drop[10m])) > 100
       labels:
         severity: minor
         service: system
       annotations:
-        summary: "CPU dropped {{ packets_dropped_minor_threshold }}{%- raw %} packets"
-        description: "The {{ $labels.cpu }} CPU on the {{ $labels.host }} node dropped {{ $value }} packets during the last 24 hours."
-    PacketsDroppedByCpuMajor:
-      {%- endraw %}
-      {%- set packets_dropped_major_threshold = monitoring.packets_dropped_per_cpu_threshold.major %}
-      if: >-
-        floor(increase(nstat_packet_drop[24h])) > {{ packets_dropped_major_threshold }}
-      labels:
-        severity: major
-        service: system
-      annotations:
-        summary: "CPU dropped {{ packets_dropped_major_threshold }}{%- raw %} packets"
-        description: "The {{ $labels.cpu }} CPU on the {{ $labels.host }} node dropped {{ $value }} packets during the last 24 hours."
+        summary: "CPU dropped more than 100 packets"
+        description: "The {{ $labels.cpu }} CPU on the {{ $labels.host }} node dropped {{ $value }} packets during the last 10 minutes."
     NetdevBudgetRanOutsWarning:
       {%- endraw %}
       {%- set squeeze_rate_threshold = monitoring.netdev_budget_squeeze_rate %}
diff --git a/linux/network/resolv.sls b/linux/network/resolv.sls
index 965ed2f..bac6ebf 100644
--- a/linux/network/resolv.sls
+++ b/linux/network/resolv.sls
@@ -11,6 +11,11 @@
   - require:
     - pkg: resolvconf
 
+ensure_resolvconf_symlink:
+  cmd.run:
+  - name: dpkg-reconfigure -fnoninteractive resolvconf
+  - unless: test -L /etc/resolv.conf
+
   {%- if network.resolv is defined %}
 /etc/resolvconf/resolv.conf.d/base:
   file.managed:
diff --git a/linux/system/auth.sls b/linux/system/auth.sls
index 690ec04..5cb3798 100644
--- a/linux/system/auth.sls
+++ b/linux/system/auth.sls
@@ -170,6 +170,7 @@
     - pkg: linux_auth_ldap_packages
   - watch_in:
     - service: linux_auth_nslcd_service
+    - service: linux_auth_nscd_service
 
 linux_auth_ldap_packages:
   pkg.installed:
@@ -185,12 +186,18 @@
     - pkg: linux_auth_ldap_packages
   - watch_in:
     - service: linux_auth_nslcd_service
+    - service: linux_auth_nscd_service
 
 linux_auth_nslcd_service:
   service.running:
   - enable: true
   - name: nslcd
 
+linux_auth_nscd_service:
+  service.running:
+  - enable: true
+  - name: nscd
+
     {%- endif %}
   {%- endif %}
 {%- endif %}
diff --git a/linux/system/file.sls b/linux/system/file.sls
index e8a6d52..1ae9906 100644
--- a/linux/system/file.sls
+++ b/linux/system/file.sls
@@ -3,16 +3,44 @@
 
 {%- for file_name, file in system.file.items() %}
 
+  {%- if file.decode is defined %}
+    {%- if file.name is defined %}
+      {%- set dirname = salt['file.dirname'](file.name) %}
+    {%- else %}
+      {%- set dirname = salt['file.dirname'](file_name) %}
+    {%- endif %}
+linux_directory_{{ dirname }}:
+  file.directory:
+    - name: {{ dirname }}
+    - makedirs: {{ file.get('makedirs', 'True') }}
+    - user: {{ file.get('user', 'root') }}
+    - group: {{ file.get('group', 'root') }}
+
 linux_file_{{ file_name }}:
-{%- if file.serialize is defined %}
+  file.decode:
+    {%- if file.name is defined %}
+    - name: {{ file.name }}
+    {%- else %}
+    - name: {{ file_name }}
+    {%- endif %}
+    - encoding_type: base64
+    {%- if file.contents_pillar is defined %}
+    - contents_pillar: {{ file.contents_pillar }}
+    {%- elif file.encoded_data is defined %}
+    - encoded_data: |
+        {{ file.encoded_data | indent(8) }}
+    {%- endif %}
+  {%- else %}
+linux_file_{{ file_name }}:
+    {%- if file.serialize is defined %}
   file.serialize:
     - formatter: {{ file.serialize }}
-  {%- if file.contents is defined  %}
+      {%- if file.contents is defined  %}
     - dataset: {{ file.contents|yaml }}
-  {%- elif file.contents_pillar is defined %}
+      {%- elif file.contents_pillar is defined %}
     - dataset_pillar: {{ file.contents_pillar }}
-  {%- endif %}
-{%- else %}
+      {%- endif %}
+    {%- else %}
   file.managed:
     {%- if file.source is defined %}
     - source: {{ file.source }}
@@ -31,8 +59,7 @@
     {%- elif file.contents_grains is defined %}
     - contents_grains: {{ file.contents_grains }}
     {%- endif %}
-
-{%- endif %}
+  {%- endif %}
     {%- if file.name is defined %}
     - name: {{ file.name }}
     {%- else %}
@@ -50,6 +77,7 @@
     {%- if file.encoding is defined %}
     - encoding: {{ file.encoding }}
     {%- endif %}
+{%- endif %}
 
 {%- endfor %}
 
diff --git a/linux/system/login_defs.sls b/linux/system/login_defs.sls
index f94348a..914e6eb 100644
--- a/linux/system/login_defs.sls
+++ b/linux/system/login_defs.sls
@@ -1,6 +1,16 @@
 {%- from "linux/map.jinja" import system with context %}
 {%- if system.enabled %}
-  {%- if system.login_defs is defined %}
+  {%- set defaults = {} %}
+  {%- set user_defaults = system.get('defaults', {}).get('user', {}) %}
+  {%- for option_name, login_defs_name in [('maxdays', 'PASS_MAX_DAYS'),
+                                           ('mindays', 'PASS_MIN_DAYS'),
+                                           ('warndays', 'PASS_WARN_AGE')] %}
+    {%- set value = user_defaults.get(option_name, None) %}
+    {%- if value != None %}
+      {%- do defaults.update({login_defs_name: value}) %}
+    {%- endif %}
+  {%- endfor %}
+  {%- if system.login_defs is defined or defaults %}
 login_defs:
   file.managed:
     - name: /etc/login.defs
@@ -9,5 +19,7 @@
     - user: root
     - group: root
     - mode: 644
+    - defaults:
+        defaults: {{ defaults|yaml }}
   {%- endif %}
 {%- endif %}
diff --git a/linux/system/repo.sls b/linux/system/repo.sls
index 73bb33d..3322f07 100644
--- a/linux/system/repo.sls
+++ b/linux/system/repo.sls
@@ -80,11 +80,11 @@
     - name: /etc/apt/preferences.d/{{ name }}
       {%- endif %}
 
-      {%- if repo.get('key') %}
+      {%- if repo.get('key') and grains['saltversioninfo'] < [2018, 3] %}
 linux_repo_{{ name }}_key:
         {% set repo_key = salt['hashutil.base64_b64encode'](repo.key) %}
-  cmd.run:
-    - name: "echo '{{ repo_key }}' | base64 -d | apt-key add -"
+    aptkey.added:
+    - key_text: {{ repo_key }}
     - require_in:
         {%- if repo.get('default', False) %}
       - file: default_repo_list
@@ -105,8 +105,8 @@
 #}
       {%- elif repo.key_url|default(False) and grains['saltversioninfo'] < [2017, 7] and not repo.key_url.startswith('salt://') %}
 linux_repo_{{ name }}_key:
-  cmd.run:
-    - name: "curl -sL {{ repo.key_url }} | apt-key add -"
+    aptkey.added:
+    - key_url: {{ repo.key_url }}
     - require_in:
         {%- if repo.get('default', False) %}
       - file: default_repo_list
@@ -144,6 +144,9 @@
             {%- if repo.key_url is defined and (grains['saltversioninfo'] >= [2017, 7] or repo.key_url.startswith('salt://')) %}
   - key_url: {{ repo.key_url }}
             {%- endif %}
+            {%- if repo.key is defined and grains['saltversioninfo'] >= [2018, 3] %}
+  - key_text: '{{ repo.key | replace("\n", "\\n") }}'
+            {%- endif %}
   - consolidate: {{ repo.get('consolidate', False) }}
   - require:
     - file: /etc/apt/apt.conf.d/99proxies-salt-{{ name }}
diff --git a/linux/system/user.sls b/linux/system/user.sls
index 64636f3..ca95c34 100644
--- a/linux/system/user.sls
+++ b/linux/system/user.sls
@@ -4,74 +4,86 @@
 include:
   - linux.system.group
 
-{%- for name, user in system.user.items() %}
-
-{%- if user.enabled %}
-
-{%- set requires = [] %}
-{%- for group in user.get('groups', []) %}
-  {%- if group in system.get('group', {}).keys() %}
-    {%- do requires.append({'group': 'system_group_'+group}) %}
+  {%- set defaults = system.get('defaults', {}).get('user', {}) %}
+  {%- if defaults %}
+etc_default_useradd:
+  file.managed:
+    - name: /etc/default/useradd
+    - source: salt://linux/files/etc_default_useradd.jinja
+    - template: jinja
+    - user: root
+    - group: root
+    - mode: 644
+    - defaults:
+        defaults: {{ defaults|yaml }}
   {%- endif %}
-{%- endfor %}
 
-{%- if user.gid is not defined %}
+  {%- for name, user in system.user.items() %}
+    {%- if user.enabled %}
+      {%- set requires = [] %}
+      {%- for group in user.get('groups', []) %}
+        {%- if group in system.get('group', {}).keys() %}
+          {%- do requires.append({'group': 'system_group_'+group}) %}
+        {%- endif %}
+      {%- endfor %}
+
+      {%- if user.gid is not defined %}
 system_group_{{ name }}:
   group.present:
   - name: {{ name }}
   - require_in:
     - user: system_user_{{ name }}
-{%- endif %}
+      {%- endif %}
 
 system_user_{{ name }}:
   user.present:
   - name: {{ name }}
   - home: {{ user.home }}
-  {% if user.get('password') == False %}
+      {% if user.get('password') == False %}
   - enforce_password: false
-  {% elif user.get('password') == None %}
+      {% elif user.get('password') == None %}
   - enforce_password: true
   - password: '*'
-  {% elif user.get('password') %}
+      {% elif user.get('password') %}
   - enforce_password: true
   - password: {{ user.password }}
   - hash_password: {{ user.get('hash_password', False) }}
-  {% endif %}
-  {%- if user.gid is defined and user.gid %}
+      {% endif %}
+      {%- if user.gid is defined and user.gid %}
   - gid: {{ user.gid }}
-  {%- else %}
+      {%- else %}
   - gid_from_name: true
-  {%- endif %}
-  {%- if user.groups is defined %}
+      {%- endif %}
+      {%- if user.groups is defined %}
   - groups: {{ user.groups }}
-  {%- endif %}
-  {%- if user.optional_groups is defined %}
+      {%- endif %}
+      {%- if user.optional_groups is defined %}
   - optional_groups: {{ user.optional_groups }}
-  {%- endif %}
-  {%- if user.system is defined and user.system %}
+      {%- endif %}
+      {%- if user.system is defined and user.system %}
   - system: True
   - shell: {{ user.get('shell', '/bin/false') }}
-  {%- else %}
+      {%- else %}
   - shell: {{ user.get('shell', '/bin/bash') }}
-  {%- endif %}
-  {%- if user.uid is defined and user.uid %}
+      {%- endif %}
+      {%- if user.uid is defined and user.uid %}
   - uid: {{ user.uid }}
-  {%- endif %}
-  {%- if user.unique is defined %}
+      {%- endif %}
+      {%- if user.unique is defined %}
   - unique: {{ user.unique }}
-  {%- endif %}
-  {%- if user.maxdays is defined %}
+      {%- endif %}
+      {%- if user.maxdays is defined %}
   - maxdays: {{ user.maxdays }}
-  {%- endif %}
-  {%- if user.mindays is defined %}
+      {%- endif %}
+      {%- if user.mindays is defined %}
   - mindays: {{ user.mindays }}
-  {%- endif %}
-  {%- if user.warndays is defined %}
+      {%- endif %}
+      {%- if user.warndays is defined %}
   - warndays: {{ user.warndays }}
-  {%- endif %}
-  {%- if user.inactdays is defined %}
+      {%- endif %}
+      {%- if user.inactdays is defined %}
   - inactdays: {{ user.inactdays }}
-  {%- endif %}
+      {%- endif %}
   - require: {{ requires|yaml }}
 
 system_user_home_{{ user.home }}:
@@ -83,7 +95,7 @@
   - require:
     - user: system_user_{{ name }}
 
-{%- if user.get('sudo', False) %}
+      {%- if user.get('sudo', False) %}
 
 /etc/sudoers.d/90-salt-user-{{ name|replace('.', '-') }}:
   file.managed:
@@ -98,14 +110,13 @@
     - user: system_user_{{ name }}
   - check_cmd: /usr/sbin/visudo -c -f
 
-{%- else %}
+      {%- else %}
 
 /etc/sudoers.d/90-salt-user-{{ name|replace('.', '-') }}:
   file.absent
 
-{%- endif %}
-
-{%- else %}
+      {%- endif %}
+    {%- else %}
 
 system_user_{{ name }}:
   user.absent:
@@ -118,8 +129,6 @@
 /etc/sudoers.d/90-salt-user-{{ name|replace('.', '-') }}:
   file.absent
 
-{%- endif %}
-
-{%- endfor %}
-
+    {%- endif %}
+  {%- endfor %}
 {%- endif %}
diff --git a/tests/pillar/system.sls b/tests/pillar/system.sls
index 73448aa..d5a953e 100644
--- a/tests/pillar/system.sls
+++ b/tests/pillar/system.sls
@@ -34,6 +34,10 @@
         name: /tmp/test3.txt
         source: salt://linux/files/test/file_template.jinja
         template: jinja
+      test4:
+        decode: True
+        name: /tmp/test4.txt
+        encoded_data: dGVzdDQK
     apt:
       preferences:
         enabled: true