1717#
1818# You should have received a copy of the GNU General Public License
1919# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
20- from __future__ import (absolute_import , division , print_function )
20+ from __future__ import absolute_import , division , print_function
21+
2122__metaclass__ = type
2223
2324DOCUMENTATION = r"""
185186display = Display ()
186187
187188
188- CONNECTION_TRANSPORT = ' kubectl'
189+ CONNECTION_TRANSPORT = " kubectl"
189190
190191CONNECTION_OPTIONS = {
191- ' kubectl_container' : '-c' ,
192- ' kubectl_namespace' : '-n' ,
193- ' kubectl_kubeconfig' : ' --kubeconfig' ,
194- ' kubectl_context' : ' --context' ,
195- ' kubectl_host' : ' --server' ,
196- ' kubectl_username' : ' --username' ,
197- ' kubectl_password' : ' --password' ,
198- ' client_cert' : ' --client-certificate' ,
199- ' client_key' : ' --client-key' ,
200- ' ca_cert' : ' --certificate-authority' ,
201- ' validate_certs' : ' --insecure-skip-tls-verify' ,
202- ' kubectl_token' : ' --token'
192+ " kubectl_container" : "-c" ,
193+ " kubectl_namespace" : "-n" ,
194+ " kubectl_kubeconfig" : " --kubeconfig" ,
195+ " kubectl_context" : " --context" ,
196+ " kubectl_host" : " --server" ,
197+ " kubectl_username" : " --username" ,
198+ " kubectl_password" : " --password" ,
199+ " client_cert" : " --client-certificate" ,
200+ " client_key" : " --client-key" ,
201+ " ca_cert" : " --certificate-authority" ,
202+ " validate_certs" : " --insecure-skip-tls-verify" ,
203+ " kubectl_token" : " --token" ,
203204}
204205
205206
206207class Connection (ConnectionBase ):
207- ''' Local kubectl based connections '''
208+ """ Local kubectl based connections """
208209
209210 transport = CONNECTION_TRANSPORT
210211 connection_options = CONNECTION_OPTIONS
@@ -217,153 +218,206 @@ def __init__(self, play_context, new_stdin, *args, **kwargs):
217218
218219 # Note: kubectl runs commands as the user that started the container.
219220 # It is impossible to set the remote user for a kubectl connection.
220- cmd_arg = ' {0}_command' .format (self .transport )
221+ cmd_arg = " {0}_command" .format (self .transport )
221222 if cmd_arg in kwargs :
222223 self .transport_cmd = kwargs [cmd_arg ]
223224 else :
224225 self .transport_cmd = distutils .spawn .find_executable (self .transport )
225226 if not self .transport_cmd :
226- raise AnsibleError ("{0} command not found in PATH" .format (self .transport ))
227+ raise AnsibleError (
228+ "{0} command not found in PATH" .format (self .transport )
229+ )
227230
228231 def _build_exec_cmd (self , cmd ):
229- """ Build the local kubectl exec command to run cmd on remote_host
230- """
232+ """Build the local kubectl exec command to run cmd on remote_host"""
231233 local_cmd = [self .transport_cmd ]
232234 censored_local_cmd = [self .transport_cmd ]
233235
234236 # Build command options based on doc string
235237 doc_yaml = AnsibleLoader (self .documentation ).get_single_data ()
236- for key in doc_yaml .get (' options' ):
237- if key .endswith (' verify_ssl' ) and self .get_option (key ) != '' :
238+ for key in doc_yaml .get (" options" ):
239+ if key .endswith (" verify_ssl" ) and self .get_option (key ) != "" :
238240 # Translate verify_ssl to skip_verify_ssl, and output as string
239241 skip_verify_ssl = not self .get_option (key )
240- local_cmd .append (u'{0}={1}' .format (self .connection_options [key ], str (skip_verify_ssl ).lower ()))
241- censored_local_cmd .append (u'{0}={1}' .format (self .connection_options [key ], str (skip_verify_ssl ).lower ()))
242- elif not key .endswith ('container' ) and self .get_option (key ) and self .connection_options .get (key ):
242+ local_cmd .append (
243+ u"{0}={1}" .format (
244+ self .connection_options [key ], str (skip_verify_ssl ).lower ()
245+ )
246+ )
247+ censored_local_cmd .append (
248+ u"{0}={1}" .format (
249+ self .connection_options [key ], str (skip_verify_ssl ).lower ()
250+ )
251+ )
252+ elif (
253+ not key .endswith ("container" )
254+ and self .get_option (key )
255+ and self .connection_options .get (key )
256+ ):
243257 cmd_arg = self .connection_options [key ]
244258 local_cmd += [cmd_arg , self .get_option (key )]
245259 # Redact password and token from console log
246- if key .endswith ((' _token' , ' _password' )):
247- censored_local_cmd += [cmd_arg , ' ********' ]
260+ if key .endswith ((" _token" , " _password" )):
261+ censored_local_cmd += [cmd_arg , " ********" ]
248262 else :
249263 censored_local_cmd += [cmd_arg , self .get_option (key )]
250264
251- extra_args_name = u' {0}_extra_args' .format (self .transport )
265+ extra_args_name = u" {0}_extra_args" .format (self .transport )
252266 if self .get_option (extra_args_name ):
253- local_cmd += self .get_option (extra_args_name ).split (' ' )
254- censored_local_cmd += self .get_option (extra_args_name ).split (' ' )
267+ local_cmd += self .get_option (extra_args_name ).split (" " )
268+ censored_local_cmd += self .get_option (extra_args_name ).split (" " )
255269
256- pod = self .get_option (u' {0}_pod' .format (self .transport ))
270+ pod = self .get_option (u" {0}_pod" .format (self .transport ))
257271 if not pod :
258272 pod = self ._play_context .remote_addr
259273 # -i is needed to keep stdin open which allows pipelining to work
260- local_cmd += [' exec' , '-i' , pod ]
261- censored_local_cmd += [' exec' , '-i' , pod ]
274+ local_cmd += [" exec" , "-i" , pod ]
275+ censored_local_cmd += [" exec" , "-i" , pod ]
262276
263277 # if the pod has more than one container, then container is required
264- container_arg_name = u' {0}_container' .format (self .transport )
278+ container_arg_name = u" {0}_container" .format (self .transport )
265279 if self .get_option (container_arg_name ):
266- local_cmd += ['-c' , self .get_option (container_arg_name )]
267- censored_local_cmd += ['-c' , self .get_option (container_arg_name )]
280+ local_cmd += ["-c" , self .get_option (container_arg_name )]
281+ censored_local_cmd += ["-c" , self .get_option (container_arg_name )]
268282
269- local_cmd += ['--' ] + cmd
270- censored_local_cmd += ['--' ] + cmd
283+ local_cmd += ["--" ] + cmd
284+ censored_local_cmd += ["--" ] + cmd
271285
272286 return local_cmd , censored_local_cmd
273287
274288 def _connect (self , port = None ):
275289 """ Connect to the container. Nothing to do """
276290 super (Connection , self )._connect ()
277291 if not self ._connected :
278- display .vvv (u"ESTABLISH {0} CONNECTION" .format (self .transport ), host = self ._play_context .remote_addr )
292+ display .vvv (
293+ u"ESTABLISH {0} CONNECTION" .format (self .transport ),
294+ host = self ._play_context .remote_addr ,
295+ )
279296 self ._connected = True
280297
281298 def exec_command (self , cmd , in_data = None , sudoable = False ):
282299 """ Run a command in the container """
283300 super (Connection , self ).exec_command (cmd , in_data = in_data , sudoable = sudoable )
284301
285- local_cmd , censored_local_cmd = self ._build_exec_cmd ([self ._play_context .executable , '-c' , cmd ])
286-
287- display .vvv ("EXEC %s" % (censored_local_cmd ,), host = self ._play_context .remote_addr )
288- local_cmd = [to_bytes (i , errors = 'surrogate_or_strict' ) for i in local_cmd ]
289- p = subprocess .Popen (local_cmd , shell = False , stdin = subprocess .PIPE ,
290- stdout = subprocess .PIPE , stderr = subprocess .PIPE )
302+ local_cmd , censored_local_cmd = self ._build_exec_cmd (
303+ [self ._play_context .executable , "-c" , cmd ]
304+ )
305+
306+ display .vvv (
307+ "EXEC %s" % (censored_local_cmd ,), host = self ._play_context .remote_addr
308+ )
309+ local_cmd = [to_bytes (i , errors = "surrogate_or_strict" ) for i in local_cmd ]
310+ p = subprocess .Popen (
311+ local_cmd ,
312+ shell = False ,
313+ stdin = subprocess .PIPE ,
314+ stdout = subprocess .PIPE ,
315+ stderr = subprocess .PIPE ,
316+ )
291317
292318 stdout , stderr = p .communicate (in_data )
293319 return (p .returncode , stdout , stderr )
294320
295321 def _prefix_login_path (self , remote_path ):
296- ''' Make sure that we put files into a standard path
322+ """ Make sure that we put files into a standard path
297323
298- If a path is relative, then we need to choose where to put it.
299- ssh chooses $HOME but we aren't guaranteed that a home dir will
300- exist in any given chroot. So for now we're choosing "/" instead.
301- This also happens to be the former default.
324+ If a path is relative, then we need to choose where to put it.
325+ ssh chooses $HOME but we aren't guaranteed that a home dir will
326+ exist in any given chroot. So for now we're choosing "/" instead.
327+ This also happens to be the former default.
302328
303- Can revisit using $HOME instead if it's a problem
304- '''
329+ Can revisit using $HOME instead if it's a problem
330+ """
305331 if not remote_path .startswith (os .path .sep ):
306332 remote_path = os .path .join (os .path .sep , remote_path )
307333 return os .path .normpath (remote_path )
308334
309335 def put_file (self , in_path , out_path ):
310336 """ Transfer a file from local to the container """
311337 super (Connection , self ).put_file (in_path , out_path )
312- display .vvv ("PUT %s TO %s" % (in_path , out_path ), host = self ._play_context .remote_addr )
338+ display .vvv (
339+ "PUT %s TO %s" % (in_path , out_path ), host = self ._play_context .remote_addr
340+ )
313341
314342 out_path = self ._prefix_login_path (out_path )
315- if not os .path .exists (to_bytes (in_path , errors = 'surrogate_or_strict' )):
316- raise AnsibleFileNotFound (
317- "file or module does not exist: %s" % in_path )
343+ if not os .path .exists (to_bytes (in_path , errors = "surrogate_or_strict" )):
344+ raise AnsibleFileNotFound ("file or module does not exist: %s" % in_path )
318345
319346 out_path = shlex_quote (out_path )
320347 # kubectl doesn't have native support for copying files into
321348 # running containers, so we use kubectl exec to implement this
322- with open (to_bytes (in_path , errors = ' surrogate_or_strict' ), 'rb' ) as in_file :
349+ with open (to_bytes (in_path , errors = " surrogate_or_strict" ), "rb" ) as in_file :
323350 if not os .fstat (in_file .fileno ()).st_size :
324- count = ' count=0'
351+ count = " count=0"
325352 else :
326- count = ''
327- args , dummy = self ._build_exec_cmd ([self ._play_context .executable , "-c" , "dd of=%s bs=%s%s" % (out_path , BUFSIZE , count )])
328- args = [to_bytes (i , errors = 'surrogate_or_strict' ) for i in args ]
353+ count = ""
354+ args , dummy = self ._build_exec_cmd (
355+ [
356+ self ._play_context .executable ,
357+ "-c" ,
358+ "dd of=%s bs=%s%s" % (out_path , BUFSIZE , count ),
359+ ]
360+ )
361+ args = [to_bytes (i , errors = "surrogate_or_strict" ) for i in args ]
329362 try :
330- p = subprocess .Popen (args , stdin = in_file ,
331- stdout = subprocess .PIPE , stderr = subprocess .PIPE )
363+ p = subprocess .Popen (
364+ args , stdin = in_file , stdout = subprocess .PIPE , stderr = subprocess .PIPE
365+ )
332366 except OSError :
333- raise AnsibleError ("kubectl connection requires dd command in the container to put files" )
367+ raise AnsibleError (
368+ "kubectl connection requires dd command in the container to put files"
369+ )
334370 stdout , stderr = p .communicate ()
335371
336372 if p .returncode != 0 :
337- raise AnsibleError ("failed to transfer file %s to %s:\n %s\n %s" % (in_path , out_path , stdout , stderr ))
373+ raise AnsibleError (
374+ "failed to transfer file %s to %s:\n %s\n %s"
375+ % (in_path , out_path , stdout , stderr )
376+ )
338377
339378 def fetch_file (self , in_path , out_path ):
340379 """ Fetch a file from container to local. """
341380 super (Connection , self ).fetch_file (in_path , out_path )
342- display .vvv ("FETCH %s TO %s" % (in_path , out_path ), host = self ._play_context .remote_addr )
381+ display .vvv (
382+ "FETCH %s TO %s" % (in_path , out_path ), host = self ._play_context .remote_addr
383+ )
343384
344385 in_path = self ._prefix_login_path (in_path )
345386 out_dir = os .path .dirname (out_path )
346387
347388 # kubectl doesn't have native support for fetching files from
348389 # running containers, so we use kubectl exec to implement this
349- args , dummy = self ._build_exec_cmd ([self ._play_context .executable , "-c" , "dd if=%s bs=%s" % (in_path , BUFSIZE )])
350- args = [to_bytes (i , errors = 'surrogate_or_strict' ) for i in args ]
390+ args , dummy = self ._build_exec_cmd (
391+ [self ._play_context .executable , "-c" , "dd if=%s bs=%s" % (in_path , BUFSIZE )]
392+ )
393+ args = [to_bytes (i , errors = "surrogate_or_strict" ) for i in args ]
351394 actual_out_path = os .path .join (out_dir , os .path .basename (in_path ))
352- with open (to_bytes (actual_out_path , errors = 'surrogate_or_strict' ), 'wb' ) as out_file :
395+ with open (
396+ to_bytes (actual_out_path , errors = "surrogate_or_strict" ), "wb"
397+ ) as out_file :
353398 try :
354- p = subprocess .Popen (args , stdin = subprocess .PIPE ,
355- stdout = out_file , stderr = subprocess .PIPE )
399+ p = subprocess .Popen (
400+ args , stdin = subprocess .PIPE , stdout = out_file , stderr = subprocess .PIPE
401+ )
356402 except OSError :
357403 raise AnsibleError (
358- "{0} connection requires dd command in the container to fetch files" .format (self .transport )
404+ "{0} connection requires dd command in the container to fetch files" .format (
405+ self .transport
406+ )
359407 )
360408 stdout , stderr = p .communicate ()
361409
362410 if p .returncode != 0 :
363- raise AnsibleError ("failed to fetch file %s to %s:\n %s\n %s" % (in_path , out_path , stdout , stderr ))
411+ raise AnsibleError (
412+ "failed to fetch file %s to %s:\n %s\n %s"
413+ % (in_path , out_path , stdout , stderr )
414+ )
364415
365416 if actual_out_path != out_path :
366- os .rename (to_bytes (actual_out_path , errors = 'strict' ), to_bytes (out_path , errors = 'strict' ))
417+ os .rename (
418+ to_bytes (actual_out_path , errors = "strict" ),
419+ to_bytes (out_path , errors = "strict" ),
420+ )
367421
368422 def close (self ):
369423 """ Terminate the connection. Nothing to do for kubectl"""
0 commit comments