Ejemplo n.º 1
0
    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)
Ejemplo n.º 2
0
    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()
Ejemplo n.º 3
0
    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)
Ejemplo n.º 4
0
    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()
Ejemplo n.º 5
0
    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
Ejemplo n.º 6
0
    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()
Ejemplo n.º 7
0
    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()
Ejemplo n.º 8
0
    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()