17
17
#
18
18
# You should have received a copy of the GNU General Public License
19
19
# 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
+
21
22
__metaclass__ = type
22
23
23
24
DOCUMENTATION = r"""
185
186
display = Display ()
186
187
187
188
188
- CONNECTION_TRANSPORT = ' kubectl'
189
+ CONNECTION_TRANSPORT = " kubectl"
189
190
190
191
CONNECTION_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" ,
203
204
}
204
205
205
206
206
207
class Connection (ConnectionBase ):
207
- ''' Local kubectl based connections '''
208
+ """ Local kubectl based connections """
208
209
209
210
transport = CONNECTION_TRANSPORT
210
211
connection_options = CONNECTION_OPTIONS
@@ -217,153 +218,206 @@ def __init__(self, play_context, new_stdin, *args, **kwargs):
217
218
218
219
# Note: kubectl runs commands as the user that started the container.
219
220
# 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 )
221
222
if cmd_arg in kwargs :
222
223
self .transport_cmd = kwargs [cmd_arg ]
223
224
else :
224
225
self .transport_cmd = distutils .spawn .find_executable (self .transport )
225
226
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
+ )
227
230
228
231
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"""
231
233
local_cmd = [self .transport_cmd ]
232
234
censored_local_cmd = [self .transport_cmd ]
233
235
234
236
# Build command options based on doc string
235
237
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 ) != "" :
238
240
# Translate verify_ssl to skip_verify_ssl, and output as string
239
241
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
+ ):
243
257
cmd_arg = self .connection_options [key ]
244
258
local_cmd += [cmd_arg , self .get_option (key )]
245
259
# 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 , " ********" ]
248
262
else :
249
263
censored_local_cmd += [cmd_arg , self .get_option (key )]
250
264
251
- extra_args_name = u' {0}_extra_args' .format (self .transport )
265
+ extra_args_name = u" {0}_extra_args" .format (self .transport )
252
266
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 (" " )
255
269
256
- pod = self .get_option (u' {0}_pod' .format (self .transport ))
270
+ pod = self .get_option (u" {0}_pod" .format (self .transport ))
257
271
if not pod :
258
272
pod = self ._play_context .remote_addr
259
273
# -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 ]
262
276
263
277
# 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 )
265
279
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 )]
268
282
269
- local_cmd += ['--' ] + cmd
270
- censored_local_cmd += ['--' ] + cmd
283
+ local_cmd += ["--" ] + cmd
284
+ censored_local_cmd += ["--" ] + cmd
271
285
272
286
return local_cmd , censored_local_cmd
273
287
274
288
def _connect (self , port = None ):
275
289
""" Connect to the container. Nothing to do """
276
290
super (Connection , self )._connect ()
277
291
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
+ )
279
296
self ._connected = True
280
297
281
298
def exec_command (self , cmd , in_data = None , sudoable = False ):
282
299
""" Run a command in the container """
283
300
super (Connection , self ).exec_command (cmd , in_data = in_data , sudoable = sudoable )
284
301
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
+ )
291
317
292
318
stdout , stderr = p .communicate (in_data )
293
319
return (p .returncode , stdout , stderr )
294
320
295
321
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
297
323
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.
302
328
303
- Can revisit using $HOME instead if it's a problem
304
- '''
329
+ Can revisit using $HOME instead if it's a problem
330
+ """
305
331
if not remote_path .startswith (os .path .sep ):
306
332
remote_path = os .path .join (os .path .sep , remote_path )
307
333
return os .path .normpath (remote_path )
308
334
309
335
def put_file (self , in_path , out_path ):
310
336
""" Transfer a file from local to the container """
311
337
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
+ )
313
341
314
342
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 )
318
345
319
346
out_path = shlex_quote (out_path )
320
347
# kubectl doesn't have native support for copying files into
321
348
# 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 :
323
350
if not os .fstat (in_file .fileno ()).st_size :
324
- count = ' count=0'
351
+ count = " count=0"
325
352
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 ]
329
362
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
+ )
332
366
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
+ )
334
370
stdout , stderr = p .communicate ()
335
371
336
372
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
+ )
338
377
339
378
def fetch_file (self , in_path , out_path ):
340
379
""" Fetch a file from container to local. """
341
380
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
+ )
343
384
344
385
in_path = self ._prefix_login_path (in_path )
345
386
out_dir = os .path .dirname (out_path )
346
387
347
388
# kubectl doesn't have native support for fetching files from
348
389
# 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 ]
351
394
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 :
353
398
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
+ )
356
402
except OSError :
357
403
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
+ )
359
407
)
360
408
stdout , stderr = p .communicate ()
361
409
362
410
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
+ )
364
415
365
416
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
+ )
367
421
368
422
def close (self ):
369
423
""" Terminate the connection. Nothing to do for kubectl"""
0 commit comments