def run_command(self, cmd, spinner=True, quiet=True): '''run_command will run a command (a list) and wrap in a spinner. A result (dict) with message and return code is returned. If the return value is not 0, an error is issed and we exit ''' if spinner is True: bot.spinner.start() result = run_cmd(cmd) if spinner is True: bot.spinner.stop() retval = result['return_code'] if quiet is False: if isinstance(result['message'], bytes): result['message'] = result['message'].decode('utf-8') print(result['message']) # Beep boop, error! if retval != 0: bot.error('Return code %s' % retval) sys.exit(retval) return result
def set_base(self, base='/', writable=True): ''' set the base (the root where to create /scif) and determine if it is writable Parameters ========== base: the full path to the root folder to create /scif ''' # The user is likely to give path to scif (should still work) base = base.strip('scif') if not os.path.exists(base): bot.error('%s does not exist!' % base) sys.exit(1) base = "/%s" % os.path.abspath(base).strip('/') self._base = os.path.join(base, 'scif') self.path_apps = '%s/apps' % self._base self.path_data = '%s/data' % self._base # Update the environment self.add_env('SCIF_DATA', self.path_data) self.add_env('SCIF_APPS', self.path_apps) # Check if it's writable if writable is True: if not os.access(base, os.W_OK): bot.error('%s is not writable.' % base) sys.exit(1)
def preview_apps(self, apps=None): '''install one or more apps to the base. If app is defined, only install app specified. Otherwise, install all found in config. ''' if apps in [None, []]: apps = self.apps() if not isinstance(apps, list): apps = [apps] for app in apps: # We must have the app defined in the config if app not in self._config['apps']: bot.error('Cannot find app %s in config.' % app) sys.exit(1) # Make directories bot.newline() settings = self._init_app_preview(app) # Get the app configuration config = self.app(app) # Handle environment, runscript, labels self._preview_runscript(app, settings, config) self._preview_environment(app, settings, config) self._preview_labels(app, settings, config) self._preview_commands(app, settings, config) self._preview_files(app, settings, config) self._preview_recipe(app, settings, config) self._preview_test(app, settings, config) bot.newline()
def mkdir_p(path): '''mkdir_p attempts to get the same functionality as mkdir -p :param path: the path to create. ''' try: os.makedirs(path) except OSError as e: if e.errno == errno.EEXIST and os.path.isdir(path): pass else: bot.error("Error creating path %s, exiting." % path) sys.exit(1)
def preview_base(self): ''' preview basic scif structure at the base for apps and metadata Parameters ========== base: the full path to the root folder to create /scif ''' if not hasattr(self, '_base'): bot.error('Please set the base before preview.') sys.exit(1) bot.custom(prefix='[base] %s' % self._base, color='CYAN') bot.custom(prefix='[apps] %s' % self.path_apps, color='CYAN') bot.custom(prefix='[data] %s\n' % self.path_data, color='CYAN')
def install_base(self): ''' create basic scif structure at the base for apps and metadata Parameters ========== base: the full path to the root folder to create /scif ''' if not hasattr(self, '_base'): bot.error('Please set the base before installing to it.') sys.exit(1) bot.info('Installing base at %s' % self._base) mkdir_p(self.path_apps) mkdir_p(self.path_data)
def get_appenv(self, app, isolated=True, update=False): '''return environment for a specific app, meaning the variables active when it is running. Parameters ========== isolated: if True, only return the active app variables (example below) update: also update the global self.environment. If isolated is True, don't include other apps. For example, for an app `hello-world-echo` and isolated True, the following is returned: { 'SCIF_APPBIN': '/scif/apps/hello-world-echo/bin', 'SCIF_APPDATA': '/scif/data/hello-world-echo', 'SCIF_APPENV': '/scif/apps/hello-world-echo/scif/environment.sh', 'SCIF_APPHELP': '/scif/apps/hello-world-echo/scif/runscript.help', 'SCIF_APPLABELS': '/scif/apps/hello-world-echo/scif/labels.json', 'SCIF_APPLIB': '/scif/apps/hello-world-echo/lib', 'SCIF_APPMETA': '/scif/apps/hello-world-echo/scif', 'SCIF_APPNAME': 'hello-world-echo', 'SCIF_APPRECIPE': '/scif/apps/hello-world-echo/scif/hello-world-echo.scif', 'SCIF_APPROOT': '/scif/apps/hello-world-echo', 'SCIF_APPRUN': '/scif/apps/hello-world-echo/scif/runscript' 'SCIF_APPTEST': '/scif/apps/hello-world-echo/scif/test.sh' } ''' final = dict() if app in self.apps(): environ = self.get_appenv_lookup(app) for var, val in environ[app].items(): updates = mk_env(key=var, val=val) final.update(updates) # The user wants to include the current SCIF environment if isolated is False and hasattr(self,'environment'): final.update(self.environment) # The user also wants to update the SCIF environment if update is True: self.environment = final return final valid = ' '.join(self.apps()) bot.error('%s is not a valid app. Found %s' %(app, valid))
def main(args, parser, subparser): from scif.main import ScifRecipe apps = args.recipe if len(apps) > 0: recipe = apps.pop(0) if not os.path.exists(recipe): bot.error("Cannot find recipe file %s" % recipe) sys.exit(1) client = ScifRecipe(recipe, writable=False) # Preview the entire recipe, or the apps chosen client.preview(apps) else: bot.info('You must provide a recipe file to preview!')
def getenv(variable_key, default=None, required=False, silent=True): ''' getenv will attempt to get an environment variable. If the variable is not found, None is returned. :param variable_key: the variable name :param required: exit with error if not found :param silent: Do not print debugging information for variable ''' variable = os.environ.get(variable_key, default) if variable is None and required: bot.error("Cannot find environment variable %s, exiting." % variable_key) sys.exit(1) if not silent and variable is not None: bot.verbose("%s found as %s" % (variable_key, variable)) return variable
def run_command(self, cmd, spinner=True): '''run_install will run a command (a list) and wrap in a spinner. A result (dict) with message and return code is returned. If the return value is not 0, an error is issed and we exit ''' if spinner is True: bot.spinner.start() result = run_cmd(cmd) if spinner is True: bot.spinner.stop() retval = result['return_code'] bot.info(result['message']) if retval != 0: bot.error('Return code %s' % retval) sys.exit(retval) return result
def install_apps(self, apps=None): '''install one or more apps to the base. If app is defined, only install app specified. Otherwise, install all found in config. ''' if apps in [None, '']: apps = self.apps() if not isinstance(apps, list): apps = [apps] if len(apps) == 0: bot.warning('No apps to install. Load a recipe or base with .load()') for app in apps: # We must have the app defined in the config if app not in self._config['apps']: bot.error('Cannot find app %s in config.' %app) sys.exit(1) # Make directories settings = self._init_app(app) # Get the app configuration config = self.app(app) # Get the app environment and export for install self.get_appenv(app, isolated=False, update=True) self.export_env(ps1=False) # Handle environment, runscript, labels self._install_runscript(app, settings, config) self._install_environment(app, settings, config) self._install_help(app, settings, config) self._install_labels(app, settings, config) self._install_files(app, settings, config) self._install_commands(app, settings, config) self._install_recipe(app, settings, config) self._install_test(app, settings, config) # After we install, in case interactive, deactivate last app self.deactivate(app)
def add_section(config, section, name=None): '''add section will add a section (and optionally) section name to a config Parameters ========== config: the config (dict) parsed thus far section: the section type (e.g., appinstall) name: an optional name, added as a level (e.g., google-drive) Resulting data structure is: config['registry']['apprun'] config[name][section] ''' if section is None: bot.error( 'You must define a section (e.g. %appenv) before any action.') sys.exit(1) if section not in sections: bot.error("%s is not a valid section." % section) sys.exit(1) global_section = 'apps' # Add the global section, if doesn't exist if global_section not in config: config[global_section] = OrderedDict() if name is not None: if name not in config[global_section]: config[global_section][name] = OrderedDict() if section not in config[global_section][name]: config[global_section][name][section] = [] bot.debug("Adding section %s %s" % (name, section)) return config
def main(args, parser, subparser): from scif.main import ScifRecipe apps = args.recipe if len(apps) == 0: bot.error("You must provide a recipe (.scif) file to install.") sys.exit(1) recipe = apps.pop(0) if not os.path.exists(recipe): bot.error("Cannot find recipe file %s" % recipe) sys.exit(1) if len(apps) == 0: apps = None client = ScifRecipe(recipe) # writable is True # Preview the entire recipe, or the apps chosen client.install(apps)
def get_appenv_lookup(self, app): '''create a dictionary with a highest level index the app name, and underneath a generic lookup (without the app name) for different variable types. Parameters ========== app: the new of the app to get the environment for isolated: if True don't include other apps Eg, app with name "google-drive" would look like: {'registry': { 'appbin': '/scif/apps/registry/bin', 'appdata': '/scif/data/registry', 'appenv': '/scif/apps/registry/scif/environment.sh', 'apphelp': '/scif/apps/registry/scif/runscript.help', 'apptest': '/scif/apps/registry/scif/test.sh', 'applabels': '/scif/apps/registry/scif/labels.json', 'applib': '/scif/apps/registry/lib', 'appmeta': '/scif/apps/registry/scif', 'apprecipe': '/scif/apps/registry/scif/registry.scif' 'approot': '/scif/apps/registry', 'apprun': '/scif/apps/registry/scif/runscript' } } This function is intended to be shared by env above and the environment generating functions in the main client, to have consistent behavior. The above data structure gets parse into the (global) variables for the particular app: {'SCIF_APPBIN_registry': '/scif/apps/registry/bin', 'SCIF_APPDATA_registry': '/scif/data/registry', 'SCIF_APPENV_registry': '/scif/apps/registry/scif/environment.sh', 'SCIF_APPHELP_registry': '/scif/apps/registry/scif/runscript.help', 'SCIF_APPLABELS_registry': '/scif/apps/registry/scif/labels.json', 'SCIF_APPTEST_registry': '/scif/apps/registry/scif/test.sh', 'SCIF_APPLIB_registry': '/scif/apps/registry/lib', 'SCIF_APPMETA_registry': '/scif/apps/registry/scif', 'SCIF_APPRECIPE_registry': '/scif/apps/registry/scif/registry.scif', 'SCIF_APPROOT_registry': '/scif/apps/registry', 'SCIF_APPRUN_registry': '/scif/apps/registry/scif/runscript'} ''' if app in self.apps(): base = self._base envars = {app:{}} # Roots for app data and app files appdata = "%s/data/%s" %(base, app) approot = "%s/apps/%s" %(base, app) appmeta = "%s/scif" %(approot) envars[app]['appdata'] = appdata envars[app]['approot'] = approot envars[app]['appmeta'] = appmeta envars[app]['appbin'] = "%s/bin" %(approot) envars[app]['applib'] = "%s/lib" %(approot) envars[app]['apprun'] = "%s/runscript" %(appmeta) envars[app]['apphelp'] = "%s/runscript.help" %(appmeta) envars[app]['appenv'] = "%s/environment.sh" %(appmeta) envars[app]['apptest'] = "%s/test.sh" %(appmeta) envars[app]['applabels'] = "%s/labels.json" %(appmeta) envars[app]['apprecipe'] = "%s/%s.scif" %(appmeta, app) envars[app]['appname'] = app return envars # if we get down here, didn't have the app in the first place valid = ' '.join(self.apps()) bot.error('%s is not a valid app. Found %s' %(app, valid))