diff --git a/changelogs/fragments/remove_chdir.yml b/changelogs/fragments/remove_chdir.yml new file mode 100644 index 00000000000..527d19d48c9 --- /dev/null +++ b/changelogs/fragments/remove_chdir.yml @@ -0,0 +1,2 @@ +bugfixes: + - removed chdir from action plugins when using local connection, moved into plugin itself to avoid future issues with threads. diff --git a/lib/ansible/plugins/action/__init__.py b/lib/ansible/plugins/action/__init__.py index f633c5c2293..752829ddf45 100644 --- a/lib/ansible/plugins/action/__init__.py +++ b/lib/ansible/plugins/action/__init__.py @@ -1054,13 +1054,9 @@ class ActionBase(with_metaclass(ABCMeta, object)): # Change directory to basedir of task for command execution when connection is local if self._connection.transport == 'local': - cwd = os.getcwd() - os.chdir(to_bytes(self._loader.get_basedir())) - try: - rc, stdout, stderr = self._connection.exec_command(cmd, in_data=in_data, sudoable=sudoable) - finally: - if self._connection.transport == 'local': - os.chdir(cwd) + self._connection.cwd = to_bytes(self._loader.get_basedir(), errors='surrogate_or_strict') + + rc, stdout, stderr = self._connection.exec_command(cmd, in_data=in_data, sudoable=sudoable) # stdout and stderr may be either a file-like or a bytes object. # Convert either one to a text type diff --git a/lib/ansible/plugins/connection/local.py b/lib/ansible/plugins/connection/local.py index edeb9f40d53..14752b2cba7 100644 --- a/lib/ansible/plugins/connection/local.py +++ b/lib/ansible/plugins/connection/local.py @@ -39,6 +39,11 @@ class Connection(ConnectionBase): transport = 'local' has_pipelining = True + def __init__(self, *args, **kwargs): + + super(Connection, self).__init__(*args, **kwargs) + self.cwd = None + def _connect(self): ''' connect to the local host; nothing to do here ''' @@ -76,7 +81,8 @@ class Connection(ConnectionBase): p = subprocess.Popen( cmd, shell=isinstance(cmd, (text_type, binary_type)), - executable=executable, # cwd=... + executable=executable, + cwd=self.cwd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, @@ -123,11 +129,19 @@ class Connection(ConnectionBase): display.debug("done with local.exec_command()") return (p.returncode, stdout, stderr) + def _ensure_abs(self, path): + if not os.path.isabs(path) and self.cwd is not None: + path = os.path.normpath(os.path.join(self.cwd, path)) + return path + def put_file(self, in_path, out_path): ''' transfer a file from local to local ''' super(Connection, self).put_file(in_path, out_path) + in_path = self._ensure_abs(in_path) + out_path = self._ensure_abs(out_path) + display.vvv(u"PUT {0} TO {1}".format(in_path, out_path), host=self._play_context.remote_addr) if not os.path.exists(to_bytes(in_path, errors='surrogate_or_strict')): raise AnsibleFileNotFound("file or module does not exist: {0}".format(to_native(in_path)))