def unpack(cls, image, dest, config=None, namespace=GLOBAL_CONF, nodeps=False, dryrun=False, update=False): """ Pull and extracts a docker image to the specified path, and loads the Nulecule application from the path. Args: image (str): A Docker image name. dest (str): Destination path where Nulecule data from Docker image should be extracted. config (dict): Dictionary, config data for Nulecule application. namespace (str): Namespace for Nulecule application. nodeps (bool): Don't pull external Nulecule dependencies when True. update (bool): Don't update contents of destination directory if False, else update it. Returns: A Nulecule instance, or None in case of dry run. """ logger.info('Unpacking image: %s to %s' % (image, dest)) if Utils.running_on_openshift(): # pass general config data containing provider specific data # to Openshift provider op = OpenShiftProvider(config.get('general', {}), './', False) op.artifacts = [] op.init() op.extract(image, APP_ENT_PATH, dest, update) else: docker_handler = DockerHandler(dryrun=dryrun) docker_handler.pull(image) docker_handler.extract(image, APP_ENT_PATH, dest, update) return cls.load_from_path( dest, config=config, namespace=namespace, nodeps=nodeps, dryrun=dryrun, update=update)
def run(self): cmdline = sys.argv[1:] # Grab args from cmdline # If we are running in an openshift pod (via `oc new-app`) then # there is no cmdline but we want to default to "atomicapp run". # In this case copy files to cwd and use the working directory. if Utils.running_on_openshift(): cmdline = 'run -v --dest=none /{}'.format(APP_ENT_PATH).split() # We want to be able to place options anywhere on the command # line. We have added all global options to each subparser, # but subparsers require all options to be after the 'action' # keyword. In order to handle this we just need to figure out # what subparser will be used and move it's keyword to the front # of the line. # NOTE: Also allow "mode" to override 'action' if specified args, _ = self.parser.parse_known_args(cmdline) cmdline.remove(args.action) # Remove 'action' from the cmdline if args.mode: args.action = args.mode # Allow mode to override 'action' cmdline.insert(0, args.action) # Place 'action' at front logger.info("Action/Mode Selected is: %s" % args.action) # Finally, parse args and give error if necessary args = self.parser.parse_args(cmdline) # Set logging level if args.verbose: set_logging(level=logging.DEBUG) elif args.quiet: set_logging(level=logging.WARNING) else: set_logging(level=logging.INFO) lock = LockFile(os.path.join(Utils.getRoot(), LOCK_FILE)) try: lock.acquire(timeout=-1) args.func(args) except AttributeError: if hasattr(args, 'func'): raise else: self.parser.print_help() except KeyboardInterrupt: pass except AlreadyLocked: logger.error("Could not proceed - there is probably another instance of Atomic App running on this machine.") except Exception as ex: if args.verbose: raise else: logger.error("Exception caught: %s", repr(ex)) logger.error( "Run the command again with -v option to get more information.") finally: if lock.i_am_locking(): lock.release()
def unpack(cls, image, dest, config=None, namespace=GLOBAL_CONF, nodeps=False, dryrun=False, update=False): """ Pull and extracts a docker image to the specified path, and loads the Nulecule application from the path. Args: image (str): A Docker image name. dest (str): Destination path where Nulecule data from Docker image should be extracted. config (dict): Dictionary, config data for Nulecule application. namespace (str): Namespace for Nulecule application. nodeps (bool): Don't pull external Nulecule dependencies when True. update (bool): Don't update contents of destination directory if False, else update it. Returns: A Nulecule instance, or None in case of dry run. """ logger.info('Unpacking image: %s to %s' % (image, dest)) if Utils.running_on_openshift(): # pass general config data containing provider specific data # to Openshift provider op = OpenShiftProvider(config.get('general', {}), './', False) op.artifacts = [] op.init() op.extract(image, APP_ENT_PATH, dest, update) else: docker_handler = DockerHandler(dryrun=dryrun) docker_handler.pull(image) docker_handler.extract(image, APP_ENT_PATH, dest, update) cockpit_logger.info("All dependencies installed successfully.") return cls.load_from_path(dest, config=config, namespace=namespace, nodeps=nodeps, dryrun=dryrun, update=update)
def run(self): cmdline = sys.argv[1:] # Grab args from cmdline # Initial setup of logging (to allow for a few early debug statements) Logging.setup_logging(verbose=True, quiet=False) # If we are running in an openshift pod (via `oc new-app`) then # there is no cmdline but we want to default to "atomicapp run". if Utils.running_on_openshift(): cmdline = 'run -v --dest=none --provider=openshift /{}' cmdline = cmdline.format(APP_ENT_PATH).split() # now a list # If the user has elected to provide all arguments via the # ATOMICAPP_ARGS environment variable then set it now argstr = os.environ.get('ATOMICAPP_ARGS') if argstr: logger.debug("Setting cmdline args to: {}".format(argstr)) cmdline = argstr.split() # If the user has elected to provide some arguments via the # ATOMICAPP_APPEND_ARGS environment variable then add those now argstr = os.environ.get('ATOMICAPP_APPEND_ARGS') if argstr: logger.debug("Appending args to cmdline: {}".format(argstr)) cmdline.extend(argstr.split()) # We want to be able to place options anywhere on the command # line. We have added all global options to each subparser, # but subparsers require all options to be after the 'action' # keyword. In order to handle this we just need to figure out # what subparser will be used and move it's keyword to the front # of the line. # NOTE: Also allow "mode" to override 'action' if specified args, _ = self.parser.parse_known_args(cmdline) cmdline.remove(args.action) # Remove 'action' from the cmdline if args.mode: args.action = args.mode # Allow mode to override 'action' cmdline.insert(0, args.action) # Place 'action' at front # Finally, parse args and give error if necessary args = self.parser.parse_args(cmdline) # Setup logging (now with arguments from cmdline) and log a few msgs Logging.setup_logging(args.verbose, args.quiet, args.logtype) logger.info("Action/Mode Selected is: %s" % args.action) logger.debug("Final parsed cmdline: {}".format(' '.join(cmdline))) # In the case of Atomic CLI we want to allow the user to specify # a directory if they want to for "run". For that reason we won't # default the RUN label for Atomic App to provide an app_spec argument. # In this case pick up app_spec from $IMAGE env var (set by RUN label). if args.app_spec is None: if os.environ.get('IMAGE') is not None: logger.debug("Setting app_spec based on $IMAGE env var") args.app_spec = os.environ['IMAGE'] else: print("Error. Too few arguments. Must provide app_spec.") print("Run with '--help' for more info") sys.exit(1) # Take the arguments that correspond to "answers" config file data # and make a dictionary of it to pass along in args. setattr(args, 'cli_answers', {}) for item in ['providerapi', 'providercafile', 'providerconfig', 'providertlsverify', 'namespace']: if hasattr(args, item) and getattr(args, item) is not None: args.cli_answers[item] = getattr(args, item) lock = LockFile(os.path.join(Utils.getRoot(), LOCK_FILE)) try: lock.acquire(timeout=-1) args.func(args) except AttributeError: if hasattr(args, 'func'): raise else: self.parser.print_help() except KeyboardInterrupt: pass except AlreadyLocked: logger.error("Could not proceed - there is probably another instance of Atomic App running on this machine.") except Exception as ex: if args.verbose: raise else: logger.error("Exception caught: %s", repr(ex)) logger.error( "Run the command again with -v option to get more information.") finally: if lock.i_am_locking(): lock.release()
def _set_config_values(self): """ Reads providerapi, namespace and accesstoken from answers.conf and corresponding values from providerconfig (if set). Use one that is set, if both are set and have conflicting values raise exception. Raises: ProviderFailedException: values in providerconfig and answers.conf are in conflict """ # First things first, if we are running inside of an openshift pod via # `oc new-app` then get the config from the environment (files/env vars) # NOTE: pick up provider_tls_verify from answers if exists if Utils.running_on_openshift(): self.providerapi = Utils.get_openshift_api_endpoint_from_env() self.namespace = os.environ['POD_NAMESPACE'] self.access_token = os.environ['TOKEN_ENV_VAR'] self.provider_ca = OPENSHIFT_POD_CA_FILE self.provider_tls_verify = \ self.config.get(PROVIDER_TLS_VERIFY_KEY, True) return # No need to process other information # initialize result to default values result = {PROVIDER_API_KEY: self.providerapi, ACCESS_TOKEN_KEY: self.access_token, NAMESPACE_KEY: self.namespace, PROVIDER_TLS_VERIFY_KEY: self.provider_tls_verify, PROVIDER_CA_KEY: self.provider_ca} # create keys in dicts and initialize values to None answers = {} providerconfig = {} for k in result.keys(): answers[k] = None providerconfig[k] = None # get values from answers.conf for k in result.keys(): answers[k] = self.config.get(k) # get values from providerconfig if self.config_file: providerconfig = self._parse_kubeconf(self.config_file) # decide between values from answers.conf and providerconfig # if only one is set use that, report if they are in conflict for k in result.keys(): if answers[k] is not None and providerconfig[k] is None: result[k] = answers[k] if answers[k] is None and providerconfig[k] is not None: result[k] = providerconfig[k] if answers[k] is not None and providerconfig[k] is not None: if answers[k] == providerconfig[k]: result[k] = answers[k] else: msg = "There are conflicting values in %s (%s) and %s (%s)"\ % (self.config_file, providerconfig[k], ANSWERS_FILE, answers[k]) logger.error(msg) raise ProviderFailedException(msg) logger.debug("config values: %s" % result) # this items are required, they have to be not None for k in [PROVIDER_API_KEY, ACCESS_TOKEN_KEY, NAMESPACE_KEY]: if result[k] is None: msg = "You need to set %s in %s" % (k, ANSWERS_FILE) logger.error(msg) raise ProviderFailedException(msg) # set config values self.providerapi = result[PROVIDER_API_KEY] self.access_token = result[ACCESS_TOKEN_KEY] self.namespace = result[NAMESPACE_KEY] self.provider_tls_verify = result[PROVIDER_TLS_VERIFY_KEY] if result[PROVIDER_CA_KEY]: # if we are in container translate path to path on host self.provider_ca = os.path.join(Utils.getRoot(), result[PROVIDER_CA_KEY].lstrip('/')) else: self.provider_ca = None
def run(self): cmdline = sys.argv[1:] # Grab args from cmdline # Initial setup of logging (to allow for a few early debug statements) Logging.setup_logging(verbose=True, quiet=False) # If we are running in an openshift pod (via `oc new-app`) then # there is no cmdline but we want to default to "atomicapp run". if Utils.running_on_openshift(): cmdline = 'run -v --dest=none --provider=openshift /{}' cmdline = cmdline.format(APP_ENT_PATH).split() # now a list # If the user has elected to provide all arguments via the # ATOMICAPP_ARGS environment variable then set it now argstr = os.environ.get('ATOMICAPP_ARGS') if argstr: logger.debug("Setting cmdline args to: {}".format(argstr)) cmdline = argstr.split() # If the user has elected to provide some arguments via the # ATOMICAPP_APPEND_ARGS environment variable then add those now argstr = os.environ.get('ATOMICAPP_APPEND_ARGS') if argstr: logger.debug("Appending args to cmdline: {}".format(argstr)) cmdline.extend(argstr.split()) # We want to be able to place options anywhere on the command # line. We have added all global options to each subparser, # but subparsers require all options to be after the 'action' # keyword. In order to handle this we just need to figure out # what subparser will be used and move it's keyword to the front # of the line. # NOTE: Also allow "mode" to override 'action' if specified args, _ = self.parser.parse_known_args(cmdline) cmdline.remove(args.action) # Remove 'action' from the cmdline if args.mode: args.action = args.mode # Allow mode to override 'action' cmdline.insert(0, args.action) # Place 'action' at front # Finally, parse args and give error if necessary args = self.parser.parse_args(cmdline) # Setup logging (now with arguments from cmdline) and log a few msgs Logging.setup_logging(args.verbose, args.quiet, args.logtype) logger.info("Action/Mode Selected is: %s" % args.action) logger.debug("Final parsed cmdline: {}".format(' '.join(cmdline))) # In the case of Atomic CLI we want to allow the user to specify # a directory if they want to for "run". For that reason we won't # default the RUN label for Atomic App to provide an app_spec argument. # In this case pick up app_spec from $IMAGE env var (set by RUN label). if args.app_spec is None: if os.environ.get('IMAGE') is not None: logger.debug("Setting app_spec based on $IMAGE env var") args.app_spec = os.environ['IMAGE'] else: print("Error. Too few arguments. Must provide app_spec.") print("Run with '--help' for more info") sys.exit(1) # Take the arguments that correspond to "answers" config file data # and make a dictionary of it to pass along in args. setattr(args, 'cli_answers', {}) for item in [ 'providerapi', 'providercafile', 'providerconfig', 'providertlsverify', 'namespace' ]: if hasattr(args, item) and getattr(args, item) is not None: args.cli_answers[item] = getattr(args, item) lock = LockFile(Utils.getLockFile()) try: lock.acquire(timeout=-1) args.func(args) except AttributeError: if hasattr(args, 'func'): raise else: self.parser.print_help() except KeyboardInterrupt: pass except AlreadyLocked: logger.error( "Could not proceed - there is probably another instance of Atomic App running on this machine." ) except Exception as ex: if args.verbose: raise else: logger.error("Exception caught: %s", repr(ex)) logger.error( "Run the command again with -v option to get more information." ) finally: if lock.i_am_locking(): lock.release()
def run(self): cmdline = sys.argv[1:] # Grab args from cmdline # If we are running in an openshift pod (via `oc new-app`) then # there is no cmdline but we want to default to "atomicapp run". if Utils.running_on_openshift(): cmdline = 'run -v --dest=none --provider=openshift /{}' cmdline = cmdline.format(APP_ENT_PATH).split() # now a list # If the user has elected to provide all arguments via the # ATOMICAPP_ARGS environment variable then set it now argstr = os.environ.get('ATOMICAPP_ARGS') if argstr: logger.debug("Setting cmdline args to: {}".format(argstr)) cmdline = argstr.split() # If the user has elected to provide some arguments via the # ATOMICAPP_APPEND_ARGS environment variable then add those now argstr = os.environ.get('ATOMICAPP_APPEND_ARGS') if argstr: logger.debug("Appending args to cmdline: {}".format(argstr)) cmdline.extend(argstr.split()) # We want to be able to place options anywhere on the command # line. We have added all global options to each subparser, # but subparsers require all options to be after the 'action' # keyword. In order to handle this we just need to figure out # what subparser will be used and move it's keyword to the front # of the line. # NOTE: Also allow "mode" to override 'action' if specified args, _ = self.parser.parse_known_args(cmdline) cmdline.remove(args.action) # Remove 'action' from the cmdline if args.mode: args.action = args.mode # Allow mode to override 'action' cmdline.insert(0, args.action) # Place 'action' at front logger.info("Action/Mode Selected is: %s" % args.action) # Finally, parse args and give error if necessary args = self.parser.parse_args(cmdline) # Take the arguments that correspond to "answers" config file data # and make a dictionary of it to pass along in args. setattr(args, 'cli_answers', {}) for item in ['providerapi', 'providercafile', 'providerconfig', 'providertlsverify']: if hasattr(args, item) and getattr(args, item) is not None: args.cli_answers[item] = getattr(args, item) # Set logging level if args.verbose: set_logging(level=logging.DEBUG) elif args.quiet: set_logging(level=logging.WARNING) else: set_logging(level=logging.INFO) # Now that we have set the logging level let's print out the cmdline logger.debug("Final parsed cmdline: {}".format(' '.join(cmdline))) lock = LockFile(os.path.join(Utils.getRoot(), LOCK_FILE)) try: lock.acquire(timeout=-1) args.func(args) except AttributeError: if hasattr(args, 'func'): raise else: self.parser.print_help() except KeyboardInterrupt: pass except AlreadyLocked: logger.error("Could not proceed - there is probably another instance of Atomic App running on this machine.") except Exception as ex: if args.verbose: raise else: logger.error("Exception caught: %s", repr(ex)) logger.error( "Run the command again with -v option to get more information.") finally: if lock.i_am_locking(): lock.release()
def __init__(self, app_spec, destination=None, answers_file=None): """ init function for NuleculeManager. Sets a few instance variables. Args: app_spec: either a path to an unpacked nulecule app or a container image name where a nulecule can be found destination: where to unpack a nulecule to if it isn't local """ # Let's pass in a default format for our answers self.answers = copy.deepcopy(DEFAULT_ANSWERS) self.answers_format = None self.answers_file = None # The path to an answer file self.app_path = None # The path where the app resides or will reside self.image = None # The container image to pull the app from # Adjust app_spec, destination, and answer file paths if absolute. if os.path.isabs(app_spec): app_spec = os.path.join(Utils.getRoot(), app_spec.lstrip('/')) if destination and os.path.isabs(destination): destination = os.path.join(Utils.getRoot(), destination.lstrip('/')) if answers_file and os.path.isabs(answers_file): answers_file = os.path.join(Utils.getRoot(), answers_file.lstrip('/')) # If the user doesn't want the files copied to a permanent # location then he provides 'none'. If that is the case we'll # use a temporary directory if destination and destination.lower() == 'none': logger.debug("'none' destination requested. Using tmp dir") destination = tempfile.mkdtemp(prefix='atomicapp') # Determine if the user passed us an image or a path to an app if not os.path.exists(app_spec): self.image = app_spec else: self.app_path = app_spec # Doesn't really make much sense to provide an app path and destination, # but if they want to we'll simply just copy the files for them if self.app_path and destination: Utils.copy_dir(self.app_path, destination, update=True) self.app_path = destination # If the user provided an image, make sure we have a destination if self.image: if destination: self.app_path = destination else: self.app_path = Utils.getNewAppCacheDir(self.image) logger.debug("NuleculeManager init app_path: %s", self.app_path) logger.debug("NuleculeManager init image: %s", self.image) # Set where the main nulecule file should be self.main_file = os.path.join(self.app_path, MAIN_FILE) # If user provided a path to answers then make sure it exists. If they # didn't provide one then use the one in the app dir if it exists. if answers_file: self.answers_file = answers_file if not os.path.isfile(self.answers_file): raise NuleculeException( "Path for answers doesn't exist: %s" % self.answers_file) else: if os.path.isfile(os.path.join(self.app_path, ANSWERS_FILE)): self.answers_file = os.path.join(self.app_path, ANSWERS_FILE) # TODO: put this in a better place in the future. # If we are running inside of an openshift pod then override # some of the config by detecting some values from the environment if Utils.running_on_openshift(): self.answers[GLOBAL_CONF]['provider'] = 'openshift' self.answers[GLOBAL_CONF]['accesstoken'] = os.environ['TOKEN_ENV_VAR'] self.answers[GLOBAL_CONF]['namespace'] = os.environ['POD_NAMESPACE'] self.answers[GLOBAL_CONF]['providerapi'] = \ Utils.get_openshift_api_endpoint_from_env()