commit 1a36c96ef83ffd16e65c018b21089a7243713e44
Author: Guy Martin <gmsoft@tuxicoman.be>
Date:   Tue Aug 6 11:21:25 2019 +0200

    Variable lock_type is not defined.

commit a94e131ac4ff8013d7983baabab7ad2e87c9e0ef
Author: Guy Martin <gmsoft@tuxicoman.be>
Date:   Tue Aug 6 11:38:24 2019 +0200

    Use global_lock() with 'with' statement.

commit 4ac976661e67fe09de09666e36d0b9e31bddc4a7
Author: Guy Martin <gmsoft@tuxicoman.be>
Date:   Tue Aug 6 13:22:41 2019 +0200

    Import GitLockError too.

commit 11968d9bb2c0bde37ac41453652dc686b8b92dfe
Author: Guy Martin <gmsoft@tuxicoman.be>
Date:   Tue Aug 6 14:44:29 2019 +0200

    Fix lint errors.

commit bd0d51211192aea831ad84e95253d8507ea1dd9e
Author: Guy Martin <gmsoft@tuxicoman.be>
Date:   Tue Aug 6 17:25:17 2019 +0200

    Use a timeout instead of failing hard and fix debug message.

commit 35cbe4fa1d1e49815782c02bf59153c3b210d2da
Author: Guy Martin <gmsoft@tuxicoman.be>
Date:   Wed Aug 7 10:19:47 2019 +0200

    Don't issue a warning message on lock contention. A debug will be enough.

Index: salt-3002.2+ds+efs/salt/pillar/git_pillar.py
===================================================================
--- salt-3002.2+ds+efs.orig/salt/pillar/git_pillar.py
+++ salt-3002.2+ds+efs/salt/pillar/git_pillar.py
@@ -395,7 +395,7 @@ import salt.utils.dictupdate
 import salt.utils.gitfs
 import salt.utils.stringutils
 import salt.utils.versions
-from salt.exceptions import FileserverConfigError
+from salt.exceptions import (FileserverConfigError, GitLockError)
 from salt.pillar import Pillar
 
 PER_REMOTE_OVERRIDES = ("base", "env", "root", "ssl_verify", "refspecs", "fallback")
@@ -446,70 +446,74 @@ def ext_pillar(minion_id, pillar, *repos
         # If masterless, fetch the remotes. We'll need to remove this once
         # we make the minion daemon able to run standalone.
         git_pillar.fetch_remotes()
-    git_pillar.global_lock()
-    git_pillar.checkout()
-    ret = {}
-    merge_strategy = __opts__.get("pillar_source_merging_strategy", "smart")
-    merge_lists = __opts__.get("pillar_merge_lists", False)
-    for pillar_dir, env in git_pillar.pillar_dirs.items():
-        # Map env if env == '__env__' before checking the env value
-        if env == "__env__":
-            env = (
-                opts.get("pillarenv")
-                or opts.get("saltenv")
-                or opts.get("git_pillar_base")
-            )
-            log.debug("__env__ maps to %s", env)
-
-        # If pillarenv is set, only grab pillars with that match pillarenv
-        if opts["pillarenv"] and env != opts["pillarenv"]:
-            log.debug(
-                "env '%s' for pillar dir '%s' does not match "
-                "pillarenv '%s', skipping",
-                env,
-                pillar_dir,
-                opts["pillarenv"],
-            )
-            continue
-        if pillar_dir in git_pillar.pillar_linked_dirs:
-            log.debug(
-                "git_pillar is skipping processing on %s as it is a " "mounted repo",
-                pillar_dir,
-            )
-            continue
-        else:
-            log.debug(
-                "git_pillar is processing pillar SLS from %s for pillar " "env '%s'",
-                pillar_dir,
-                env,
-            )
-
-        pillar_roots = [pillar_dir]
-
-        if __opts__["git_pillar_includes"]:
-            # Add the rest of the pillar_dirs in this environment to the
-            # list, excluding the current pillar_dir being processed. This
-            # is because it was already specified above as the first in the
-            # list, so that its top file is sourced from the correct
-            # location and not from another git_pillar remote.
-            pillar_roots.extend(
-                [
-                    d
-                    for (d, e) in git_pillar.pillar_dirs.items()
-                    if env == e and d != pillar_dir
-                ]
-            )
-
-        opts["pillar_roots"] = {env: pillar_roots}
-
-        local_pillar = Pillar(opts, __grains__, minion_id, env)
-        ret = salt.utils.dictupdate.merge(
-            ret,
-            local_pillar.compile_pillar(ext=False),
-            strategy=merge_strategy,
-            merge_lists=merge_lists,
-        )
-    return ret
+    try:
+        with git_pillar.global_lock():
+            git_pillar.checkout()
+            ret = {}
+            merge_strategy = __opts__.get("pillar_source_merging_strategy", "smart")
+            merge_lists = __opts__.get("pillar_merge_lists", False)
+            for pillar_dir, env in git_pillar.pillar_dirs.items():
+                # Map env if env == '__env__' before checking the env value
+                if env == "__env__":
+                    env = (
+                        opts.get("pillarenv")
+                        or opts.get("saltenv")
+                        or opts.get("git_pillar_base")
+                    )
+                    log.debug("__env__ maps to %s", env)
+
+                # If pillarenv is set, only grab pillars with that match pillarenv
+                if opts["pillarenv"] and env != opts["pillarenv"]:
+                    log.debug(
+                        "env '%s' for pillar dir '%s' does not match "
+                        "pillarenv '%s', skipping",
+                        env,
+                        pillar_dir,
+                        opts["pillarenv"],
+                    )
+                    continue
+                if pillar_dir in git_pillar.pillar_linked_dirs:
+                    log.debug(
+                        "git_pillar is skipping processing on %s as it is a " "mounted repo",
+                        pillar_dir,
+                    )
+                    continue
+                else:
+                    log.debug(
+                        "git_pillar is processing pillar SLS from %s for pillar " "env '%s'",
+                        pillar_dir,
+                        env,
+                    )
+
+                pillar_roots = [pillar_dir]
+
+                if __opts__["git_pillar_includes"]:
+                    # Add the rest of the pillar_dirs in this environment to the
+                    # list, excluding the current pillar_dir being processed. This
+                    # is because it was already specified above as the first in the
+                    # list, so that its top file is sourced from the correct
+                    # location and not from another git_pillar remote.
+                    pillar_roots.extend(
+                        [
+                            d
+                            for (d, e) in git_pillar.pillar_dirs.items()
+                            if env == e and d != pillar_dir
+                        ]
+                    )
+
+                opts["pillar_roots"] = {env: pillar_roots}
+
+                local_pillar = Pillar(opts, __grains__, minion_id, env)
+                ret = salt.utils.dictupdate.merge(
+                    ret,
+                    local_pillar.compile_pillar(ext=False),
+                    strategy=merge_strategy,
+                    merge_lists=merge_lists,
+                )
+            return ret
+    except GitLockError:
+        log.error("Could not obtain the global lock for pillar procssing")
+    return False
 
 
 def _extract_key_val(kv, delimiter="="):
Index: salt-3002.2+ds+efs/salt/utils/gitfs.py
===================================================================
--- salt-3002.2+ds+efs.orig/salt/utils/gitfs.py
+++ salt-3002.2+ds+efs/salt/utils/gitfs.py
@@ -875,7 +875,7 @@ class GitProvider:
                                 "cache, the lock may have been obtained "
                                 "by another master."
                             )
-                    log.warning(msg)
+                    log.debug(msg)
                     if failhard:
                         raise
                     return
@@ -3276,6 +3276,56 @@ class GitPillar(GitBase):
                             )
                             time.sleep(poll_interval)
                             continue
+        finally:
+            for repo in locked_repo:
+                repo.clear_lock(lock_type='global')
+
+    @contextlib.contextmanager
+    def global_lock(self, timeout=60, poll_interval=0.5):
+        '''
+        Set and automatically clear a lock on all the repos
+        '''
+
+        # Make sure that we have a positive integer timeout, otherwise just set
+        # it to zero.
+        try:
+            timeout = int(timeout)
+        except ValueError:
+            timeout = 0
+        else:
+            if timeout < 0:
+                timeout = 0
+
+        if not isinstance(poll_interval, (six.integer_types, float)) \
+                or poll_interval < 0:
+            poll_interval = 0.5
+
+        if poll_interval > timeout:
+            poll_interval = timeout
+
+        locked_repo = []
+        try:
+            time_start = time.time()
+            for repo in self.remotes:
+                while True:
+                    try:
+                        repo._lock(lock_type='global', failhard=True)
+                        locked_repo.append(repo)
+                        yield
+                        # Break out of his loop once we've yielded the lock, to
+                        # avoid continued attempts to iterate and establish lock
+                        break
+                    except (OSError, IOError, GitLockError) as exc:
+                        if not timeout or time.time() - time_start > timeout:
+                            raise GitLockError(exc.errno, exc.strerror)
+                        else:
+                            log.debug(
+                                'A global lock is already present for %s remote '
+                                '\'%s\', sleeping %f second(s)',
+                                self.role, remote.id, poll_interval
+                            )
+                            time.sleep(poll_interval)
+                            continue
         finally:
             for repo in locked_repo:
                 repo.clear_lock(lock_type='global')
