Repository: iterative/dvc

Issue:
ssh: error during push causing bad traceback
# Description

When pushing to a remote SSH the following error occurred (truncated to only relevant output):

```
DEBUG: SELECT count from state_info WHERE rowid=1
DEBUG: fetched: [(33301,)]
DEBUG: UPDATE state_info SET count = 33301 WHERE rowid = 1
DEBUG: Path /home/rob/fossid/dvc/autoid-dvc/.dvc/cache inode 6554341
DEBUG: INSERT OR REPLACE INTO state(inode, size, mtime, timestamp, md5) VALUES (6554341, "44515650", "1554877442274173184", "1554879861734038784", "")
ERROR: failed to push data to the cloud - 'host'
------------------------------------------------------------
Traceback (most recent call last):
  File "/home/rob/fossid/dvc/.venv/lib/python3.6/site-packages/dvc/remote/ssh/__init__.py", line 187, in upload
    from_info["path"], to_info["path"], progress_title=name
  File "/home/rob/fossid/dvc/.venv/lib/python3.6/site-packages/dvc/remote/ssh/connection.py", line 189, in upload
    self.makedirs(posixpath.dirname(dest))
  File "/home/rob/fossid/dvc/.venv/lib/python3.6/site-packages/dvc/remote/ssh/connection.py", line 127, in makedirs
    self._sftp.mkdir(path)
  File "/home/rob/fossid/dvc/.venv/lib/python3.6/site-packages/paramiko/sftp_client.py", line 460, in mkdir
    self._request(CMD_MKDIR, path, attr)
  File "/home/rob/fossid/dvc/.venv/lib/python3.6/site-packages/paramiko/sftp_client.py", line 813, in _request
    return self._read_response(num)
  File "/home/rob/fossid/dvc/.venv/lib/python3.6/site-packages/paramiko/sftp_client.py", line 865, in _read_response
    self._convert_status(msg)
  File "/home/rob/fossid/dvc/.venv/lib/python3.6/site-packages/paramiko/sftp_client.py", line 898, in _convert_status
    raise IOError(text)
OSError: Failure

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/rob/fossid/dvc/.venv/lib/python3.6/site-packages/dvc/command/data_sync.py", line 66, in do_run
    recursive=self.args.recursive,
  File "/home/rob/fossid/dvc/.venv/lib/python3.6/site-packages/dvc/repo/push.py", line 27, in push
    used, jobs, remote=remote, show_checksums=show_checksums
  File "/home/rob/fossid/dvc/.venv/lib/python3.6/site-packages/dvc/data_cloud.py", line 132, in push
    show_checksums=show_checksums,
  File "/home/rob/fossid/dvc/.venv/lib/python3.6/site-packages/dvc/remote/local.py", line 713, in push
    download=False,
  File "/home/rob/fossid/dvc/.venv/lib/python3.6/site-packages/dvc/remote/local.py", line 703, in _process
    f.result()
  File "/usr/lib/python3.6/concurrent/futures/_base.py", line 425, in result
    return self.__get_result()
  File "/usr/lib/python3.6/concurrent/futures/_base.py", line 384, in __get_result
    raise self._exception
  File "/usr/lib/python3.6/concurrent/futures/thread.py", line 56, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/home/rob/fossid/dvc/.venv/lib/python3.6/site-packages/dvc/remote/ssh/__init__.py", line 192, in upload
    host=from_info["host"],
KeyError: 'host'
------------------------------------------------------------

Having any troubles?. Hit us up at https://dvc.org/support, we are always happy to help!
DEBUG: Analytics is enabled.
DEBUG: Trying to spawn '['/home/rob/fossid/dvc/.venv/bin/python3', '-m', 'dvc', 'daemon', '-q', 'analytics', '/tmp/tmplvf38zmb']'
DEBUG: Spawned '['/home/rob/fossid/dvc/.venv/bin/python3', '-m', 'dvc', 'daemon', '-q', 'analytics', '/tmp/tmplvf38zmb']'
```

# Information about setup

DVC version(i.e. `dvc --version`), Platform and method of installation (pip, homebrew, pkg Mac, exe (Windows), DEB(Linux), RPM(Linux))

```
$ dvc --version
0.35.5
```
DVC installed via pip.

```
$ python3 --version
Python 3.6.7
```
```
$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 18.04.2 LTS
Release:	18.04
Codename:	bionic
```





Discussion:
Related https://discordapp.com/channels/485586884165107732/485596304961962003/565444183977099264

Resolving patch:
diff --git a/dvc/remote/ssh/connection.py b/dvc/remote/ssh/connection.py
--- a/dvc/remote/ssh/connection.py
+++ b/dvc/remote/ssh/connection.py
@@ -1,6 +1,7 @@
 import os
 import posixpath
 import logging
+import errno
 import stat
 from contextlib import contextmanager
 
@@ -96,10 +97,13 @@ def islink(self, path):
     def makedirs(self, path):
         self._sftp_connect()
 
-        if self.isdir(path):
+        # Single stat call will say whether this is a dir, a file or a link
+        st_mode = self.st_mode(path)
+
+        if stat.S_ISDIR(st_mode):
             return
 
-        if self.isfile(path) or self.islink(path):
+        if stat.S_ISREG(st_mode) or stat.S_ISLNK(st_mode):
             raise DvcException(
                 "a file with the same name '{}' already exists".format(path)
             )
@@ -110,7 +114,13 @@ def makedirs(self, path):
             self.makedirs(head)
 
         if tail:
-            self._sftp.mkdir(path)
+            try:
+                self._sftp.mkdir(path)
+            except IOError as e:
+                # Since paramiko errors are very vague we need to recheck
+                # whether it's because path already exists or something else
+                if e.errno == errno.EACCES or not self.exists(path):
+                    raise
 
     def walk(self, directory, topdown=True):
         # NOTE: original os.walk() implementation [1] with default options was


Test patch: