Repository: spack/spack

Issue:
Lock unit test failing on Travis MacOS builder
It seems we have a failure on MacOS, started after we merged #7692 - see [here](https://travis-ci.org/spack/spack/jobs/381414674) for an example log. Not sure if the error is transient (i.e. due to Travis itself) or not, logging the failure here to have it on the records.

### Expected Result

Green light from Travis

### Actual Result

```console
____________________________ test_lock_checks_user _____________________________
tmpdir = local('/private/var/folders/bb/n7t3rs157850byt_jfdcq9k80000gn/T/pytest-of-travis/pytest-0/test_lock_checks_user0')
    def test_lock_checks_user(tmpdir):
        """Ensure lock checks work."""
        path = str(tmpdir)
        uid = os.getuid()
    
        # self-owned, own group
>       tmpdir.chown(uid, uid)
lib/spack/spack/test/llnl/util/lock.py:944: 
...
<omitted>
...
E           EPERM: [Operation not permitted]: chown('/private/var/folders/bb/n7t3rs157850byt_jfdcq9k80000gn/T/pytest-of-travis/pytest-0/test_lock_checks_user0', 501, 501)
lib/spack/external/py/_error.py:85: EPERM
======== 1 failed, 1035 passed, 76 skipped, 1 xfailed in 852.13 seconds ========
```




Discussion:
I don't have access to a Mac to fix this, so pinging @tgamblin @scheibelp 
I logged into the travis builder.  The issue is that on macs, your primary `gid` is NOT the same as your `uid`, so the `chmod` fails with a permission error.  This succeeds on LLNL macs because we use AD, and we do have things set up so primary `gid` == `uid`.  Oops.

The test should get the primary gid from the `grp` module.  I'll submit a PR for this.
@adamjstewart, @alalazo: Is it worth turning macs back on in the regular build now?  Seems like the response time is reasonably fast on mac builders, but I haven't been keeping up with travis status.

Resolving patch:
diff --git a/lib/spack/llnl/util/filesystem.py b/lib/spack/llnl/util/filesystem.py
--- a/lib/spack/llnl/util/filesystem.py
+++ b/lib/spack/llnl/util/filesystem.py
@@ -27,8 +27,10 @@
 import hashlib
 import fileinput
 import glob
+import grp
 import numbers
 import os
+import pwd
 import re
 import shutil
 import stat
@@ -212,6 +214,21 @@ def set_install_permissions(path):
         os.chmod(path, 0o644)
 
 
+def group_ids(uid=None):
+    """Get group ids that a uid is a member of.
+
+    Arguments:
+        uid (int): id of user, or None for current user
+
+    Returns:
+        (list of int): gids of groups the user is a member of
+    """
+    if uid is None:
+        uid = os.getuid()
+    user = pwd.getpwuid(uid).pw_name
+    return [g.gr_gid for g in grp.getgrall() if user in g.gr_mem]
+
+
 def copy_mode(src, dest):
     """Set the mode of dest to that of src unless it is a link.
     """


Test patch:
diff --git a/lib/spack/spack/test/cmd/debug.py b/lib/spack/spack/test/cmd/debug.py
--- a/lib/spack/spack/test/cmd/debug.py
+++ b/lib/spack/spack/test/cmd/debug.py
@@ -35,8 +35,9 @@ def test_create_db_tarball(tmpdir, database):
     with tmpdir.as_cwd():
         debug('create-db-tarball')
 
+        # get the first non-dotfile to avoid coverage files in the directory
         files = os.listdir(os.getcwd())
-        tarball_name = files[0]
+        tarball_name = next(f for f in files if not f.startswith('.'))
 
         # debug command made an archive
         assert os.path.exists(tarball_name)
diff --git a/lib/spack/spack/test/llnl/util/lock.py b/lib/spack/spack/test/llnl/util/lock.py
--- a/lib/spack/spack/test/llnl/util/lock.py
+++ b/lib/spack/spack/test/llnl/util/lock.py
@@ -72,10 +72,9 @@
 
 import pytest
 
-from llnl.util.filesystem import touch
+from llnl.util.filesystem import touch, group_ids
 
 import spack.util.lock
-from spack.util.executable import which
 from spack.util.multiproc import Barrier
 from spack.util.lock import Lock, WriteTransaction, ReadTransaction, LockError
 
@@ -936,14 +935,16 @@ def test_disable_locking(private_lock_path):
 
 
 def test_lock_checks_user(tmpdir):
-    """Ensure lock checks work."""
-    path = str(tmpdir)
+    """Ensure lock checks work with a self-owned, self-group repo."""
     uid = os.getuid()
+    if uid not in group_ids():
+        pytest.skip("user has no group with gid == uid")
 
     # self-owned, own group
     tmpdir.chown(uid, uid)
 
     # safe
+    path = str(tmpdir)
     tmpdir.chmod(0o744)
     spack.util.lock.check_lock_safety(path)
 
@@ -966,23 +967,17 @@ def test_lock_checks_user(tmpdir):
 
 
 def test_lock_checks_group(tmpdir):
-    path = str(tmpdir)
+    """Ensure lock checks work with a self-owned, non-self-group repo."""
     uid = os.getuid()
-
-    id_cmd = which('id')
-    if not id_cmd:
-        pytest.skip("Can't determine user's groups.")
-
-    # find a legal gid to user that is NOT the user's uid
-    gids = [int(gid) for gid in id_cmd('-G', output=str).split(' ')]
-    gid = next((g for g in gids if g != uid), None)
-    if gid is None:
-        pytest.skip("Can't determine user's groups.")
+    gid = next((g for g in group_ids() if g != uid), None)
+    if not gid:
+        pytest.skip("user has no group with gid != uid")
 
     # self-owned, another group
     tmpdir.chown(uid, gid)
 
     # safe
+    path = str(tmpdir)
     tmpdir.chmod(0o744)
     spack.util.lock.check_lock_safety(path)