11#!/usr/bin/env python3
22import sys
33import re
4+
45from os .path import basename , dirname , join , exists
56from collections import OrderedDict
67from typing import Union
@@ -276,7 +277,17 @@ def get_info(host, *, users=None, connection=None):
276277 data ["role" ] = "hub" if discovery .get ("NTD_CFHUB" ) else "client"
277278
278279 data ["bin" ] = {}
279- for bin in ["dpkg" , "rpm" , "yum" , "apt" , "pkg" , "zypper" , "curl" ]:
280+ for bin in [
281+ "dpkg" ,
282+ "rpm" ,
283+ "yum" ,
284+ "apt" ,
285+ "pkg" ,
286+ "zypper" ,
287+ "curl" ,
288+ "wget" ,
289+ "sha256sum" ,
290+ ]:
280291 path = discovery .get ("NTD_{}" .format (bin .upper ()))
281292 if path :
282293 data ["bin" ][bin ] = path
@@ -432,7 +443,7 @@ def bootstrap_host(host_data, policy_server, *, connection=None, trust_server=Tr
432443def _package_from_list (tags , extension , packages ):
433444 artifacts = [Artifact (None , p ) for p in packages ]
434445 artifact = filter_artifacts (artifacts , tags , extension )[- 1 ]
435- return artifact .url
446+ return artifact .url , artifact
436447
437448
438449def _package_from_releases (
@@ -445,7 +456,7 @@ def _package_from_releases(
445456 release = releases .pick_version (version )
446457 if release is None :
447458 print ("Could not find a release for version {}" .format (version ))
448- return None
459+ return None , None
449460
450461 release .init_download ()
451462
@@ -455,7 +466,7 @@ def _package_from_releases(
455466 version , edition
456467 )
457468 )
458- return None
469+ return None , None
459470
460471 artifacts = release .find (tags , extension )
461472 if not artifacts :
@@ -464,13 +475,16 @@ def _package_from_releases(
464475 "hub" if "hub" in tags else "client"
465476 )
466477 )
467- return None
478+ return None , None
468479 artifact = artifacts [- 1 ]
469480 if remote_download :
470- return artifact .url
481+ return artifact .url , artifact
471482 else :
472- return download_package (
473- artifact .url , checksum = artifact .checksum , insecure = insecure
483+ return (
484+ download_package (
485+ artifact .url , checksum = artifact .checksum , insecure = insecure
486+ ),
487+ artifact ,
474488 )
475489
476490
@@ -514,13 +528,103 @@ def get_package_from_host_info(
514528 tags .extend (tag for tag in package_tags if tag != "msi" )
515529
516530 if packages is None : # No command line argument given
517- package = _package_from_releases (
531+ package , artifact = _package_from_releases (
518532 tags , extension , version , edition , remote_download , insecure
519533 )
520534 else :
521- package = _package_from_list (tags , extension , packages )
535+ package , artifact = _package_from_list (tags , extension , packages )
536+
537+ return package , artifact
538+
522539
523- return package
540+ def _remote_download (
541+ host , package , artifact , avalaible_binaries , insecure = False , connection = None
542+ ):
543+ """
544+ Remotely download package on host from url.
545+
546+ This function checks for avalaible binaries, copies remote-download.sh to the host, then runs it to download the package from its url.
547+ The script ensures package integrity by comparing checksums.
548+
549+ :host str hostname
550+ :package str package url
551+ :artifact Artifact artifact corresponding to the package that is downloaded
552+ :avalaible_binaries Dict[str:str] dictionary containing of program_name:path on the host
553+ """
554+
555+ if not avalaible_binaries :
556+ log .error ("No binary could be found on host '{}'" .format (host ))
557+ return None
558+
559+ if "sha256sum" not in avalaible_binaries :
560+ if not insecure :
561+ log .error (
562+ "Cannot check file integrity. sha256sum is not installed on host '{}'. Run with --insecure to skip" .format (
563+ host
564+ )
565+ )
566+ return None
567+ log .warning (
568+ "Cannot check file integrity. sha256sum is not installed on host '{}'. Continuing due to insecure flag" .format (
569+ host
570+ )
571+ )
572+
573+ if not any (bin in avalaible_binaries for bin in ("wget" , "curl" )):
574+ log .error (
575+ "Cannot download remotely. wget and curl are not installed on host '{}'" .format (
576+ host
577+ )
578+ )
579+ return None
580+ use_curl = "wget" not in avalaible_binaries
581+
582+ if not (artifact and artifact .checksum ):
583+ if not insecure :
584+ log .error (
585+ "Cannot check file integrity on '{}'. No artifact associated with package '{}' found. Run with --insecure to skip" .format (
586+ host , package
587+ )
588+ )
589+ return None
590+ log .warning (
591+ "Cannot check file integrity on '{}'. No artifact associated with package '{}' found. Continuing due to insecure flag" .format (
592+ host , package
593+ )
594+ )
595+
596+ cf_remote_dir = dirname (__file__ )
597+ script_path = join (cf_remote_dir , "remote-download.sh" )
598+ if not exists (script_path ):
599+ sys .exit ("%s does not exist" % script_path )
600+ scp (
601+ script_path ,
602+ host ,
603+ connection ,
604+ hide = True ,
605+ )
606+
607+ args = ""
608+ if insecure :
609+ args += "-I "
610+ if use_curl :
611+ args += "-C "
612+ if artifact :
613+ args += "-c {} " .format (artifact .checksum )
614+ args += package
615+
616+ ret = ssh_cmd (connection , "bash remote-download.sh {}" .format (args ), errors = True )
617+
618+ if ret is None :
619+ return None
620+ if insecure :
621+ warning = re .search (r"Package.*" , ret )
622+ if warning :
623+ log .warning (warning .group (0 ))
624+
625+ log .debug ("Package '{}' downloaded successfully on host '{}'" .format (package , host ))
626+
627+ return basename (package )
524628
525629
526630@auto_connect
@@ -552,9 +656,10 @@ def install_host(
552656 elif packages and len (packages ) == 1 :
553657 package = packages [0 ]
554658
659+ artifact = None
555660 if not package :
556661 try :
557- package = get_package_from_host_info (
662+ package , artifact = get_package_from_host_info (
558663 data .get ("package_tags" ),
559664 data .get ("bin" ),
560665 data .get ("arch" ),
@@ -574,19 +679,16 @@ def install_host(
574679 return 1
575680
576681 if remote_download :
577- if ("bin" not in data ) or ("curl" not in data ["bin" ]):
578- log .error (
579- "Couldn't download remotely. Curl is not installed on host '%s'" % host
580- )
581- return 1
582-
583- print ("Downloading '%s' on '%s' using curl" % (package , host ))
584- r = ssh_cmd (
585- cmd = "curl --fail -O {}" .format (package ), connection = connection , errors = True
682+ package = _remote_download (
683+ host ,
684+ package ,
685+ artifact ,
686+ data .get ("bin" ),
687+ connection = connection ,
688+ insecure = insecure ,
586689 )
587- if r is None :
690+ if package is None :
588691 return 1
589- package = basename (package )
590692 elif not getattr (connection , "is_local" , False ):
591693 scp (package , host , connection = connection )
592694 package = basename (package )
0 commit comments