git-p4: better error reporting when p4 fails
authorLuke Diamand <luke@diamand.org>
Fri, 8 Jun 2018 20:32:45 +0000 (21:32 +0100)
committerJunio C Hamano <gitster@pobox.com>
Tue, 12 Jun 2018 21:46:09 +0000 (14:46 -0700)
Currently when p4 fails to run, git-p4 just crashes with an obscure
error message.

For example, if the P4 ticket has expired, you get:

  Error: Cannot locate perforce checkout of <path> in client view

This change checks whether git-p4 can talk to the Perforce server when
the first P4 operation is attempted, and tries to print a meaningful
error message if it fails.

Signed-off-by: Luke Diamand <luke@diamand.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
git-p4.py
t/t9833-errors.sh [new file with mode: 0755]

index 037d5d3..cbb6619 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -50,6 +50,8 @@ defaultLabelRegexp = r'[a-zA-Z0-9_\-.]+$'
 # Grab changes in blocks of this many revisions, unless otherwise requested
 defaultBlockSize = 512
 
+p4_access_checked = False
+
 def p4_build_cmd(cmd):
     """Build a suitable p4 command line.
 
@@ -91,6 +93,13 @@ def p4_build_cmd(cmd):
         real_cmd = ' '.join(real_cmd) + ' ' + cmd
     else:
         real_cmd += cmd
+
+    # now check that we can actually talk to the server
+    global p4_access_checked
+    if not p4_access_checked:
+        p4_access_checked = True    # suppress access checks in p4_check_access itself
+        p4_check_access()
+
     return real_cmd
 
 def git_dir(path):
@@ -264,6 +273,52 @@ def p4_system(cmd):
     if retcode:
         raise CalledProcessError(retcode, real_cmd)
 
+def die_bad_access(s):
+    die("failure accessing depot: {0}".format(s.rstrip()))
+
+def p4_check_access(min_expiration=1):
+    """ Check if we can access Perforce - account still logged in
+    """
+    results = p4CmdList(["login", "-s"])
+
+    if len(results) == 0:
+        # should never get here: always get either some results, or a p4ExitCode
+        assert("could not parse response from perforce")
+
+    result = results[0]
+
+    if 'p4ExitCode' in result:
+        # p4 returned non-zero status, e.g. P4PORT invalid, or p4 not in path
+        die_bad_access("could not run p4")
+
+    code = result.get("code")
+    if not code:
+        # we get here if we couldn't connect and there was nothing to unmarshal
+        die_bad_access("could not connect")
+
+    elif code == "stat":
+        expiry = result.get("TicketExpiration")
+        if expiry:
+            expiry = int(expiry)
+            if expiry > min_expiration:
+                # ok to carry on
+                return
+            else:
+                die_bad_access("perforce ticket expires in {0} seconds".format(expiry))
+
+        else:
+            # account without a timeout - all ok
+            return
+
+    elif code == "error":
+        data = result.get("data")
+        if data:
+            die_bad_access("p4 error: {0}".format(data))
+        else:
+            die_bad_access("unknown error")
+    else:
+        die_bad_access("unknown error code {0}".format(code))
+
 _p4_version_string = None
 def p4_version_string():
     """Read the version string, showing just the last line, which
diff --git a/t/t9833-errors.sh b/t/t9833-errors.sh
new file mode 100755 (executable)
index 0000000..9ba892d
--- /dev/null
@@ -0,0 +1,78 @@
+#!/bin/sh
+
+test_description='git p4 errors'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+       start_p4d
+'
+
+test_expect_success 'add p4 files' '
+       (
+               cd "$cli" &&
+               echo file1 >file1 &&
+               p4 add file1 &&
+               p4 submit -d "file1"
+       )
+'
+
+# after this test, the default user requires a password
+test_expect_success 'error handling' '
+       git p4 clone --dest="$git" //depot@all &&
+       (
+               cd "$git" &&
+               P4PORT=: test_must_fail git p4 submit 2>errmsg
+       ) &&
+       p4 passwd -P newpassword &&
+       (
+               P4PASSWD=badpassword test_must_fail git p4 clone //depot/foo 2>errmsg &&
+               grep -q "failure accessing depot.*P4PASSWD" errmsg
+       )
+'
+
+test_expect_success 'ticket logged out' '
+       P4TICKETS="$cli/tickets" &&
+       echo "newpassword" | p4 login &&
+       (
+               cd "$git" &&
+               test_commit "ticket-auth-check" &&
+               p4 logout &&
+               test_must_fail git p4 submit 2>errmsg &&
+               grep -q "failure accessing depot" errmsg
+       )
+'
+
+test_expect_success 'create group with short ticket expiry' '
+       P4TICKETS="$cli/tickets" &&
+       echo "newpassword" | p4 login &&
+       p4_add_user short_expiry_user &&
+       p4 -u short_expiry_user passwd -P password &&
+       p4 group -i <<-EOF &&
+       Group: testgroup
+       Timeout: 3
+       Users: short_expiry_user
+       EOF
+
+       p4 users | grep short_expiry_user
+'
+
+test_expect_success 'git operation with expired ticket' '
+       P4TICKETS="$cli/tickets" &&
+       P4USER=short_expiry_user &&
+       echo "password" | p4 login &&
+       (
+               cd "$git" &&
+               git p4 sync &&
+               sleep 5 &&
+               test_must_fail git p4 sync 2>errmsg &&
+               grep "failure accessing depot" errmsg
+       )
+'
+
+test_expect_success 'kill p4d' '
+       kill_p4d
+'
+
+
+test_done