def _get_path_to_storage(self) -> str: cmd = F'{self.builder} storage-path' basep = utils.chomp(host.capture(cmd)) # Now build up the entire image path. The convention appears to be: # basep/img/tag specn = self.config['tag'] return os.path.join(basep, 'img', specn)
def _emit_build_spec(self) -> None: dockerf = self.config['spec'] # Add spec file to the metadata assets. metadata.add_asset(metadata.FileAsset(dockerf)) # Emit the contents of the spec file. logger.log('# Begin Spec Output') logger.log(utils.chomp(str().join(utils.cat(dockerf)))) logger.log('# End Spec Output')
def run( # pylint: disable=too-many-arguments cmd: str, verbatim: bool = False, echo: bool = False, capture_output: bool = False, verbose: bool = True, check_exit_code: bool = True) -> List[str]: ''' Executes the provided command. Returns newline-delimited list of output if capture_output if True. Throws ChildProcessError on error if check_exit_code is True. ''' def getrealcmd(cmd: str, verbatim: bool) -> str: # The user wants us to run the string exactly as provided. if verbatim: return cmd return F'{constants.BASH_MAGIC} {shlex.quote(cmd)}' realcmd = getrealcmd(cmd, verbatim) if echo: logger.log(F'# $ {realcmd}') # Output list of strings used to (optionally) capture command output. olst: List[str] = list() with subprocess.Popen( realcmd, shell=True, # nosec bufsize=1, # Enables text mode, making write() et al. happy. universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) as spo: # Show progress and store output to a string (if requested). while True: stdout = spo.stdout.readline() if not stdout: break if capture_output: olst.append(stdout) if verbose: logger.log(utils.chomp(stdout)) wrc = spo.wait() if wrc != os.EX_OK and check_exit_code: cpe = ChildProcessError() cpe.errno = wrc estr = F"Command '{realcmd}' returned non-zero exit status." cpe.strerror = estr raise cpe return olst
def capture(cmd: str, check_exit_code: bool = True) -> str: ''' Executes the provided command and returns a string with the command's output. See run() for exceptions. ''' res = run(cmd, capture_output=True, verbose=False, check_exit_code=check_exit_code) return utils.chomp(str().join(res))
def os_pretty_name() -> str: ''' Returns the host's pretty name as reported by /etc/os-release. ''' name = 'Unknown' try: with open('/etc/os-release') as osrel: for line in osrel: if not line.startswith('PRETTY_NAME='): continue name = utils.chomp(line.split('=')[1]).strip('"') break except (OSError, IOError): pass return name
def readgs( gspath: str, config: Optional[CLIConfiguration] = None ) -> Iterable[str]: ''' A convenience routine for reading generate specification files. TODO(skg) Add description of formatting rules, semantics, etc. Don't forget about yield! We accept the following forms: # -a/--aarg [ARG_PARAMS] -b/--bargs [ARG PARAMS] # -c/--carg [ARG PARAMS] [positional arguments] ''' logger.emlog(F'# Reading Generate Specification File: {gspath}') # Emit contents of gs file. logger.log('# Begin Generate Specification') logger.log(utils.chomp(str().join(utils.cat(gspath)))) logger.log('# End Generate Specification\n') with open(gspath) as file: argv = list() lines = [x.strip() for x in utils.read_logical_lines(file)] for line in lines: # Interpret as special comment used to specify run-time arguments. if line.startswith('# -'): # Add to argument list. if config is not None: argv.extend(shlex.split(line.lstrip('# '))) continue # Skip comments and empty lines. if line.startswith('#') or utils.emptystr(line): continue # Parse arguments if provided an argument parser. gsargs = None if config is not None: if not isinstance(config, CLIConfiguration): estr = F'{__name__} expects an instance of CLIConfiguration' raise ValueError(estr) gsargs = parsedargs(config.argparser, argv) config.update(gsargs) # Not a comment; yield generate specification string. yield line # Clear out argument list for next round. argv = list()
def capture( cmd: str, check_exit_code: bool = True ) -> str: ''' Executes the provided command and returns a string with the command's output. See run() for exceptions. ''' runo = cntrimg.activator().run( [cmd], echo=False, verbose=False, capture=True, check_exit_code=check_exit_code ) return utils.chomp(str().join(runo))
def _check_env(self) -> None: ''' Build environment verification function. Raises OSError if the environment is unsatisfactory. ''' logger.emlog('# Checking your build environment...') inyp = 'Is it in your PATH?\n' notf = "'{}' not found. " + inyp errs = str() if not host.which(self.buildc): errs += notf.format(self.buildc) if not host.which(self.tarcmd): errs += notf.format(self.tarcmd) if errs: raise OSError(utils.chomp(errs))
def _get_path_to_storage(self) -> str: cmd = '{} -b {} -t {} --print-storage {}'.format( self.buildc, self.builder, self.config['tag'], self.config['spec'] ) cmdo = utils.chomp(host.capture(cmd)) # Now do some filtering because the output emits more than just the # storage path. lst = list(filter(lambda x: 'building with' not in x, cmdo.split('\n'))) if len(lst) < 1: msg = 'Could not determine storage ' \ 'path from the following output:\n{}' raise RuntimeError(msg.format(cmdo)) # Hope for the best because of the rudimentary filtering used (i.e., # hope lst[0] is a valid path). If this ever becomes problematic, # implement something nicer. Also, not a huge deal because the returned # value will be used in later file operations (let them fail). basep = lst[0] # Now build up the entire image path. The convention appears to be: # basep/img/tag specn = '{}'.format(self.config['tag']) return os.path.join(basep, 'img', specn)