def init(self): self.namespace = "default" self.k8s_manifests = [] logger.debug("Given config: %s", self.config) if self.config.get("namespace"): self.namespace = self.config.get("namespace") logger.info("Using namespace %s", self.namespace) if self.container: self.kubectl = self._find_kubectl(Utils.getRoot()) kube_conf_path = "/etc/kubernetes" if not os.path.exists(kube_conf_path): if self.dryrun: logger.info("DRY-RUN: link %s from %s%s" % (kube_conf_path, HOST_DIR, kube_conf_path)) else: os.symlink( os.path.join(Utils.getRoot(), kube_conf_path.lstrip("/")), kube_conf_path) else: self.kubectl = self._find_kubectl() if not self.dryrun: if not os.access(self.kubectl, os.X_OK): raise ProviderFailedException("Command: " + self.kubectl + " not found") # Check if Kubernetes config file is accessible self.checkConfigFile()
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 """ 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('/')) # 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 make sense to provide an app path and destination if self.app_path and destination: raise NuleculeException( "You can't provide a local path and 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)
def init(self): self.namespace = "default" self.k8s_manifests = [] logger.debug("Given config: %s", self.config) if self.config.get("namespace"): self.namespace = self.config.get("namespace") logger.info("Using namespace %s", self.namespace) if self.container: self.kubectl = self._find_kubectl(Utils.getRoot()) kube_conf_path = "/etc/kubernetes" host_kube_conf_path = os.path.join(Utils.getRoot(), kube_conf_path.lstrip("/")) if not os.path.exists(kube_conf_path) and os.path.exists(host_kube_conf_path): if self.dryrun: logger.info("DRY-RUN: link %s from %s" % (kube_conf_path, host_kube_conf_path)) else: os.symlink(host_kube_conf_path, kube_conf_path) else: self.kubectl = self._find_kubectl() if not self.dryrun: if not os.access(self.kubectl, os.X_OK): raise ProviderFailedException("Command: " + self.kubectl + " not found") # Check if Kubernetes config file is accessible, but only # if one was provided by the user; config file is optional. if self.config_file: self.checkConfigFile()
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 """ 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('/')) # 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 make sense to provide an app path and destination if self.app_path and destination: raise NuleculeException( "You can't provide a local path and 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)
def run(self): self.set_arguments() args = self.parser.parse_args() 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 init(self): self.namespace = "default" self.k8s_manifests = [] logger.debug("Given config: %s", self.config) if self.config.get("namespace"): self.namespace = self.config.get("namespace") logger.info("Using namespace %s", self.namespace) if self.container: self.kubectl = self._find_kubectl(Utils.getRoot()) kube_conf_path = "/etc/kubernetes" host_kube_conf_path = Utils.get_real_abspath(kube_conf_path) if not os.path.exists(kube_conf_path) and os.path.exists(host_kube_conf_path): if self.dryrun: logger.info("DRY-RUN: link %s from %s" % (kube_conf_path, host_kube_conf_path)) else: os.symlink(host_kube_conf_path, kube_conf_path) else: self.kubectl = self._find_kubectl() if not self.dryrun: if not os.access(self.kubectl, os.X_OK): raise ProviderFailedException("Command: " + self.kubectl + " not found") # Check if Kubernetes config file is accessible, but only # if one was provided by the user; config file is optional. if self.config_file: self.checkConfigFile()
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 init(self): self.cli = find_executable(self.cli_str) if self.container and not self.cli: host_path = [] for path in os.environ.get("PATH").split(":"): host_path.append(os.path.join(Utils.getRoot(), path.lstrip("/"))) self.cli = find_binary(self.cli_str, path=":".join(host_path)) if not self.cli: # if run as non-root we need a symlink in the container os.symlink(os.path.join(Utils.getRoot(), "usr/bin/oc"), "/usr/bin/oc") self.cli = "/usr/bin/oc" if not self.dryrun: if not self.cli or not os.access(self.cli, os.X_OK): raise ProviderFailedException("Command %s not found" % self.cli) else: logger.debug("Using %s to run OpenShift commands.", self.cli) # Check if the required OpenShift config file is accessible. self.checkConfigFile()
def init(self): self.cli = find_executable(self.cli_str) if self.container and not self.cli: host_path = [] for path in os.environ.get("PATH").split(":"): host_path.append(os.path.join(Utils.getRoot(), path.lstrip("/"))) self.cli = find_binary(self.cli_str, path=":".join(host_path)) if not self.cli: # if run as non-root we need a symlink in the container os.symlink(os.path.join(Utils.getRoot(), "usr/bin/oc"), "/usr/bin/oc") self.cli = "/usr/bin/oc" if not self.dryrun: if not self.cli or not os.access(self.cli, os.X_OK): raise ProviderFailedException("Command %s not found" % self.cli) else: logger.debug("Using %s to run OpenShift commands.", self.cli) # Check if OpenShift config file is accessible self.checkConfigFile()
def init(self): self.oc_artifacts = {} logger.debug("Given config: %s", self.config) if self.config.get("namespace"): self.namespace = self.config.get("namespace") logger.info("Using namespace %s", self.namespace) self._process_artifacts() if self.dryrun: return ''' Config_file: If a config_file has been provided, use the configuration from the file and load the associated generated file. If a config_file exists (--provider-config) use that. Params: If any provider specific parameters have been provided, load the configuration through the answers.conf file .kube/config: If no config file or params are provided by user then try to find and use a config file at the default location. no config at all: If no .kube/config file can be found then try to connect to the default unauthenticated http://localhost:8080/api end-point. ''' default_config_loc = os.path.join(Utils.getRoot(), Utils.getUserHome().strip('/'), '.kube/config') if self.config_file: logger.debug("Provider configuration provided") self.api = Client(KubeConfig.from_file(self.config_file), "openshift") elif self._check_required_params(): logger.debug("Generating .kube/config from given parameters") self.api = Client(self._from_required_params(), "openshift") elif os.path.isfile(default_config_loc): logger.debug( ".kube/config exists, using default configuration file") self.api = Client(KubeConfig.from_file(default_config_loc), "openshift") else: self.config["provider-api"] = OC_DEFAULT_API self.api = Client(self._from_required_params(), "openshift") self._check_namespaces()
def init(self): self.k8s_artifacts = {} logger.debug("Given config: %s", self.config) if self.config.get("namespace"): self.namespace = self.config.get("namespace") logger.info("Using namespace %s", self.namespace) self._process_artifacts() if self.dryrun: return ''' Config_file: If a config_file has been provided, use the configuration from the file and load the associated generated file. If a config_file exists (--provider-config) use that. Params: If any provider specific parameters have been provided, load the configuration through the answers.conf file .kube/config: If no config file or params are provided by user then try to find and use a config file at the default location. no config at all: If no .kube/config file can be found then try to connect to the default unauthenticated http://localhost:8080/api end-point. ''' default_config_loc = os.path.join( Utils.getRoot(), Utils.getUserHome().strip('/'), '.kube/config') if self.config_file: logger.debug("Provider configuration provided") self.api = Client(KubeConfig.from_file(self.config_file), "kubernetes") elif self._check_required_params(): logger.debug("Generating .kube/config from given parameters") self.api = Client(self._from_required_params(), "kubernetes") elif os.path.isfile(default_config_loc): logger.debug(".kube/config exists, using default configuration file") self.api = Client(KubeConfig.from_file(default_config_loc), "kubernetes") else: self.config["provider-api"] = K8S_DEFAULT_API self.api = Client(self._from_required_params(), "kubernetes") # Check if the namespace that the app is being deployed to is available self._check_namespaces()
def init(self): self.namespace = "default" self.k8s_manifests = [] logger.debug("Given config: %s", self.config) if self.config.get("namespace"): self.namespace = self.config.get("namespace") logger.info("Using namespace %s", self.namespace) if self.container: self.kubectl = self._find_kubectl(Utils.getRoot()) kube_conf_path = "/etc/kubernetes" if not os.path.exists(kube_conf_path): if self.dryrun: logger.info("DRY-RUN: link %s from %s%s" % (kube_conf_path, HOST_DIR, kube_conf_path)) else: os.symlink(os.path.join(Utils.getRoot(), kube_conf_path.lstrip("/")), kube_conf_path) else: self.kubectl = self._find_kubectl() if not self.dryrun: if not os.access(self.kubectl, os.X_OK): raise ProviderFailedException("Command: " + self.kubectl + " not found")
def run(self): self.set_arguments() # Set our arguments cmdline = sys.argv[1:] # Grab args from cmdline # 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. args, _ = self.parser.parse_known_args(cmdline) cmdline.remove(args.action) # Remove 'action' from the cmdline cmdline.insert(0, args.action) # Place 'action' at front # Finally, parse args and give error if necessary args = self.parser.parse_args(cmdline) 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 run(self): self.set_arguments() args = self.parser.parse_args() 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 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 __init__(self, app_spec, destination=None, cli_answers=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 cli_answers: some answer file values provided from cli args answers_file: the location of the answers file """ self.answers = copy.deepcopy(DEFAULT_ANSWERS) self.cli_answers = cli_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) # Create the app_path if it doesn't exist yet if not os.path.isdir(self.app_path): os.makedirs(self.app_path) # Set where the main nulecule file should be self.main_file = os.path.join(self.app_path, MAIN_FILE) # Process answers. self.answers_file = answers_file self._process_answers()
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()
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()