Browse code

Add explicit substitution of relative paths on the remote host just in case the connection programs do something unexpected.

Toshio Kuratomi authored on 2015/10/04 09:24:57
Showing 4 changed files
... ...
@@ -21,6 +21,7 @@ __metaclass__ = type
21 21
 
22 22
 import distutils.spawn
23 23
 import os
24
+import os.path
24 25
 import subprocess
25 26
 import traceback
26 27
 
... ...
@@ -83,8 +84,6 @@ class Connection(ConnectionBase):
83 83
         local_cmd = [self.chroot_cmd, self.chroot, executable, '-c', cmd]
84 84
 
85 85
         self._display.vvv("EXEC %s" % (local_cmd), host=self.chroot)
86
-        # FIXME: cwd= needs to be set to the basedir of the playbook, which
87
-        #        should come from loader, but is not in the connection plugins
88 86
         p = subprocess.Popen(local_cmd, shell=False, stdin=stdin,
89 87
                 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
90 88
 
... ...
@@ -99,12 +98,26 @@ class Connection(ConnectionBase):
99 99
         stdout, stderr = p.communicate(in_data)
100 100
         return (p.returncode, stdout, stderr)
101 101
 
102
+    def _prefix_login_path(self, remote_path):
103
+        ''' Make sure that we put files into a standard path
104
+
105
+            If a path is relative, then we need to choose where to put it.
106
+            ssh chooses $HOME but we aren't guaranteed that a home dir will
107
+            exist in any given chroot.  So for now we're choosing "/" instead.
108
+            This also happens to be the former default.
109
+
110
+            Can revisit using $HOME instead if it's a problem
111
+        '''
112
+        if not remote_path.startswith(os.path.sep):
113
+            remote_path = os.path.join(os.path.sep, remote_path)
114
+        return os.path.normpath(remote_path)
115
+
102 116
     def put_file(self, in_path, out_path):
103 117
         ''' transfer a file from local to chroot '''
104 118
         super(Connection, self).put_file(in_path, out_path)
105
-
106 119
         self._display.vvv("PUT %s TO %s" % (in_path, out_path), host=self.chroot)
107 120
 
121
+        out_path = self._prefix_login_path(out_path)
108 122
         try:
109 123
             with open(in_path, 'rb') as in_file:
110 124
                 try:
... ...
@@ -124,9 +137,9 @@ class Connection(ConnectionBase):
124 124
     def fetch_file(self, in_path, out_path):
125 125
         ''' fetch a file from chroot to local '''
126 126
         super(Connection, self).fetch_file(in_path, out_path)
127
-
128 127
         self._display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self.chroot)
129 128
 
129
+        in_path = self._prefix_login_path(in_path)
130 130
         try:
131 131
             p = self._buffered_exec_command('dd if=%s bs=%s' % (in_path, BUFSIZE))
132 132
         except OSError:
... ...
@@ -26,19 +26,20 @@ __metaclass__ = type
26 26
 
27 27
 import distutils.spawn
28 28
 import os
29
+import os.path
29 30
 import subprocess
30 31
 import re
31 32
 
32 33
 from distutils.version import LooseVersion
33 34
 
34 35
 import ansible.constants as C
35
-
36 36
 from ansible.errors import AnsibleError, AnsibleFileNotFound
37 37
 from ansible.plugins.connection import ConnectionBase
38 38
 
39 39
 BUFSIZE = 65536
40 40
 
41 41
 class Connection(ConnectionBase):
42
+    ''' Local docker based connections '''
42 43
 
43 44
     transport = 'docker'
44 45
     has_pipelining = True
... ...
@@ -53,9 +54,11 @@ class Connection(ConnectionBase):
53 53
         # Note: docker supports running as non-root in some configurations.
54 54
         # (For instance, setting the UNIX socket file to be readable and
55 55
         # writable by a specific UNIX group and then putting users into that
56
-        # group).  But if the user is getting a permission denied error it
57
-        # probably means that docker on their system is only configured to be
58
-        # connected to by root and they are not running as root.
56
+        # group).  Therefore we don't check that the user is root when using
57
+        # this connection.  But if the user is getting a permission denied
58
+        # error it probably means that docker on their system is only
59
+        # configured to be connected to by root and they are not running as
60
+        # root.
59 61
 
60 62
         if 'docker_command' in kwargs:
61 63
             self.docker_cmd = kwargs['docker_command']
... ...
@@ -79,7 +82,6 @@ class Connection(ConnectionBase):
79 79
     def _get_docker_version(self):
80 80
 
81 81
         cmd = [self.docker_cmd, 'version']
82
-
83 82
         cmd_output = subprocess.check_output(cmd)
84 83
 
85 84
         for line in cmd_output.split('\n'):
... ...
@@ -106,7 +108,7 @@ class Connection(ConnectionBase):
106 106
             self._connected = True
107 107
 
108 108
     def exec_command(self, cmd, in_data=None, sudoable=False):
109
-        """ Run a command on the local host """
109
+        """ Run a command on the docker host """
110 110
         super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
111 111
 
112 112
         executable = C.DEFAULT_EXECUTABLE.split()[0] if C.DEFAULT_EXECUTABLE else '/bin/sh'
... ...
@@ -114,22 +116,32 @@ class Connection(ConnectionBase):
114 114
         local_cmd = [self.docker_cmd, "exec", '-i', self._play_context.remote_addr, executable, '-c', cmd]
115 115
 
116 116
         self._display.vvv("EXEC %s" % (local_cmd), host=self._play_context.remote_addr)
117
-        # FIXME: cwd= needs to be set to the basedir of the playbook, which
118
-        #        should come from loader, but is not in the connection plugins
119
-        p = subprocess.Popen(local_cmd,
120
-                             shell=False,
121
-                             stdin=subprocess.PIPE,
117
+        p = subprocess.Popen(local_cmd, shell=False, stdin=subprocess.PIPE,
122 118
                              stdout=subprocess.PIPE, stderr=subprocess.PIPE)
123 119
 
124 120
         stdout, stderr = p.communicate(in_data)
125 121
         return (p.returncode, stdout, stderr)
126 122
 
123
+    def _prefix_login_path(self, remote_path):
124
+        ''' Make sure that we put files into a standard path
125
+
126
+            If a path is relative, then we need to choose where to put it.
127
+            ssh chooses $HOME but we aren't guaranteed that a home dir will
128
+            exist in any given chroot.  So for now we're choosing "/" instead.
129
+            This also happens to be the former default.
130
+
131
+            Can revisit using $HOME instead if it's a problem
132
+        '''
133
+        if not remote_path.startswith(os.path.sep):
134
+            remote_path = os.path.join(os.path.sep, remote_path)
135
+        return os.path.normpath(remote_path)
136
+
127 137
     def put_file(self, in_path, out_path):
128
-        """ Transfer a file from local to container """
138
+        """ Transfer a file from local to docker container """
129 139
         super(Connection, self).put_file(in_path, out_path)
130
-
131 140
         self._display.vvv("PUT %s TO %s" % (in_path, out_path), host=self._play_context.remote_addr)
132 141
 
142
+        out_path = self._prefix_login_path(out_path)
133 143
         if not os.path.exists(in_path):
134 144
             raise AnsibleFileNotFound(
135 145
                 "file or module does not exist: %s" % in_path)
... ...
@@ -161,9 +173,9 @@ class Connection(ConnectionBase):
161 161
     def fetch_file(self, in_path, out_path):
162 162
         """ Fetch a file from container to local. """
163 163
         super(Connection, self).fetch_file(in_path, out_path)
164
-
165 164
         self._display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self._play_context.remote_addr)
166 165
 
166
+        in_path = self._prefix_login_path(in_path)
167 167
         # out_path is the final file path, but docker takes a directory, not a
168 168
         # file path
169 169
         out_dir = os.path.dirname(out_path)
... ...
@@ -22,6 +22,7 @@ __metaclass__ = type
22 22
 
23 23
 import distutils.spawn
24 24
 import os
25
+import os.path
25 26
 import subprocess
26 27
 import traceback
27 28
 
... ...
@@ -67,8 +68,6 @@ class Connection(ConnectionBase):
67 67
         return cmd
68 68
 
69 69
     def list_jails(self):
70
-        # FIXME: cwd= needs to be set to the basedir of the playbook, which
71
-        #        should come from loader, but is not in the connection plugins
72 70
         p = subprocess.Popen([self.jls_cmd, '-q', 'name'],
73 71
                              stdin=subprocess.PIPE,
74 72
                              stdout=subprocess.PIPE, stderr=subprocess.PIPE)
... ...
@@ -78,8 +77,6 @@ class Connection(ConnectionBase):
78 78
         return stdout.split()
79 79
 
80 80
     def get_jail_path(self):
81
-        # FIXME: cwd= needs to be set to the basedir of the playbook, which
82
-        #        should come from loader, but is not in the connection plugins
83 81
         p = subprocess.Popen([self.jls_cmd, '-j', self.jail, '-q', 'path'],
84 82
                              stdin=subprocess.PIPE,
85 83
                              stdout=subprocess.PIPE, stderr=subprocess.PIPE)
... ...
@@ -107,8 +104,6 @@ class Connection(ConnectionBase):
107 107
         local_cmd = [self.jexec_cmd, self.jail, executable, '-c', cmd]
108 108
 
109 109
         self._display.vvv("EXEC %s" % (local_cmd), host=self.jail)
110
-        # FIXME: cwd= needs to be set to the basedir of the playbook, which
111
-        #        should come from loader, but is not in the connection plugins
112 110
         p = subprocess.Popen(local_cmd, shell=False, stdin=stdin,
113 111
                 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
114 112
 
... ...
@@ -130,11 +125,26 @@ class Connection(ConnectionBase):
130 130
         stdout, stderr = p.communicate(in_data)
131 131
         return (p.returncode, stdout, stderr)
132 132
 
133
+    def _prefix_login_path(self, remote_path):
134
+        ''' Make sure that we put files into a standard path
135
+
136
+            If a path is relative, then we need to choose where to put it.
137
+            ssh chooses $HOME but we aren't guaranteed that a home dir will
138
+            exist in any given chroot.  So for now we're choosing "/" instead.
139
+            This also happens to be the former default.
140
+
141
+            Can revisit using $HOME instead if it's a problem
142
+        '''
143
+        if not remote_path.startswith(os.path.sep):
144
+            remote_path = os.path.join(os.path.sep, remote_path)
145
+        return os.path.normpath(remote_path)
146
+
133 147
     def put_file(self, in_path, out_path):
134 148
         ''' transfer a file from local to jail '''
135 149
         super(Connection, self).put_file(in_path, out_path)
136 150
         self._display.vvv("PUT %s TO %s" % (in_path, out_path), host=self.jail)
137 151
 
152
+        out_path = self._prefix_login_path(out_path)
138 153
         try:
139 154
             with open(in_path, 'rb') as in_file:
140 155
                 try:
... ...
@@ -156,6 +166,7 @@ class Connection(ConnectionBase):
156 156
         super(Connection, self).fetch_file(in_path, out_path)
157 157
         self._display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self.jail)
158 158
 
159
+        in_path = self._prefix_login_path(in_path)
159 160
         try:
160 161
             p = self._buffered_exec_command('dd if=%s bs=%s' % (in_path, BUFSIZE))
161 162
         except OSError:
... ...
@@ -23,6 +23,7 @@ __metaclass__ = type
23 23
 
24 24
 import distutils.spawn
25 25
 import os
26
+import os.path
26 27
 import subprocess
27 28
 import traceback
28 29
 
... ...
@@ -116,8 +117,6 @@ class Connection(ConnectionBase):
116 116
         local_cmd = [self.zlogin_cmd, self.zone, executable, '-c', cmd]
117 117
 
118 118
         self._display.vvv("EXEC %s" % (local_cmd), host=self.zone)
119
-        # FIXME: cwd= should be set to the basedir of the playbook, which
120
-        # should come from loader but is not in the connection plugins
121 119
         p = subprocess.Popen(local_cmd, shell=False, stdin=stdin,
122 120
                 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
123 121
 
... ...
@@ -139,11 +138,26 @@ class Connection(ConnectionBase):
139 139
         stdout, stderr = p.communicate(in_data)
140 140
         return (p.returncode, stdout, stderr)
141 141
 
142
+    def _prefix_login_path(self, remote_path):
143
+        ''' Make sure that we put files into a standard path
144
+
145
+            If a path is relative, then we need to choose where to put it.
146
+            ssh chooses $HOME but we aren't guaranteed that a home dir will
147
+            exist in any given chroot.  So for now we're choosing "/" instead.
148
+            This also happens to be the former default.
149
+
150
+            Can revisit using $HOME instead if it's a problem
151
+        '''
152
+        if not remote_path.startswith(os.path.sep):
153
+            remote_path = os.path.join(os.path.sep, remote_path)
154
+        return os.path.normpath(remote_path)
155
+
142 156
     def put_file(self, in_path, out_path):
143 157
         ''' transfer a file from local to zone '''
144 158
         super(Connection, self).put_file(in_path, out_path)
145 159
         self._display.vvv("PUT %s TO %s" % (in_path, out_path), host=self.zone)
146 160
 
161
+        out_path = self._prefix_login_path(out_path)
147 162
         try:
148 163
             with open(in_path, 'rb') as in_file:
149 164
                 try:
... ...
@@ -165,6 +179,7 @@ class Connection(ConnectionBase):
165 165
         super(Connection, self).fetch_file(in_path, out_path)
166 166
         self._display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self.zone)
167 167
 
168
+        in_path = self._prefix_login_path(in_path)
168 169
         try:
169 170
             p = self._buffered_exec_command('dd if=%s bs=%s' % (in_path, BUFSIZE))
170 171
         except OSError: