229 lines
6.4 KiB
Python

import glob
import logging
import os
import sys
from collections import namedtuple
from copy import copy
from pprint import pprint
from types import SimpleNamespace
from xdg import BaseDirectory
from . import exceptions as error
from .catalog import Catalog
from .framework import DictItem, scoped_ident
from .idents import Idents
from .lib.utils import (filter_existing_files, get_root_pkg_dir, import_module,
open_yaml, to_yaml)
from .meta import NAME, VERSION
from .providers import Providers
logger = logging.getLogger(__name__)
# IamException = error.IamException
class App:
name = NAME
def __init__(self, ident="_", config_path=None, cli_context=None):
self.cli_ctx = cli_context
# Load application
self.init_db(config_path)
self.init_ident(ident)
def init_ident(self, ident):
"Init application with ident"
self.ident_raw = ident
view = scoped_ident(ident)
# Save important vars
self.ident_name = view.ident
self.ident_scope = view.scope
# Fetch ident config
if ident == "_" or not ident:
logger.debug("Enable default ident")
self.ident = self.idents.items_class("default")
else:
self.ident = self.idents.get(self.ident_name)
# Load user and catalog
self.catalog = Catalog(self, self.ident, self.cli_ctx)
def load_config_files(self, config_path=None):
"Fetch config from default place"
config_candidates = []
# Load file or config
if config_path is not None:
path = config_path
else:
path = os.path.join(BaseDirectory.xdg_config_home, self.name)
self.config_dir = path
if os.path.isdir(path):
# files = glob.glob("*.yml", root_dir=path) # Python > 3.10
files = glob.glob(f"{path}/*.yml")
for file_ in files:
# file_ = os.path.join(path, file_) # Python > 3.10, see above
payloads = open_yaml(file_)
for payload in payloads:
config_candidates.append(
(
file_,
payload,
)
)
if len(config_candidates) == 0:
_msg = f"Can't find any yaml configuration in: {path}"
raise error.MissingConfigFiles(_msg)
config = {}
for cand in config_candidates:
path, conf = cand
config.update(conf)
return config
def init_db(self, config_path):
"Load database"
# Update python PATH for later plugin loading
self.conf_plugin_dir = os.path.join(config_path, "plugins")
sys.path.insert(0, self.conf_plugin_dir)
# Init Databases
db = self.load_config_files(config_path=config_path)
self.db = db
self._load_plugins(db.get("plugins", []))
# Load providers
providers_conf = {}
providers_conf.update(self.plugin_providers)
# pprint(providers_conf)
providers_conf.update(db.get("providers", {}))
self.providers = Providers("ConfigProviders", providers_conf)
# Load idents
idents_conf = {
# "_": {},
}
idents_conf.update(db.get("idents", {}))
self.idents = Idents("ConfigIdents", idents_conf)
def _load_plugins(self, plugins_refs):
"Load plugins"
plugins = {}
for plugin_name in plugins_refs:
providers = self.parse_plugin_def(plugin_name)
plugins.update(providers)
# print (to_yaml(mod))
self.plugin_providers = plugins
def parse_plugin_def(self, plugin_name):
mod_name = plugin_name
mod_comp = "__all__"
if ":" in plugin_name:
mod_name, mod_comp = plugin_name.split(":", 2)
plugin_name = f"{mod_name}:{mod_comp}"
# Load plugin
providers = None
matches = filter_existing_files(
self.conf_plugin_dir, [f"{mod_name}.yml", f"{mod_name}.yaml"]
)
if matches:
logger.debug(f"Load YAML plugin {mod_name}")
providers = open_yaml(matches[0])[0].get("providers")
else:
try:
mod = import_module(mod_name)
logger.debug(f"Load Python plugin {mod_name}")
providers = mod.providers
except ModuleNotFoundError:
pass
# pprint(providers)
# Report empty plugins
if not providers:
_msg = f"Impossible to load module: {plugin_name}"
raise error.ConfigUnknownPlugin(_msg)
# Select only required items
if not plugin_name.endswith(":__all__"):
item = providers.get(mod_comp)
providers = {mod_comp: item}
return providers
def user_dump(self, pattern=None):
"Dump ident config"
self.catalog.dump()
def get_idents(self, extended=False):
"Return list of identities"
ret = list(self.idents.get_idents())
if extended:
out = ["mrjk/backups", "mrjk/pictures"]
ret.extend(out)
return ret
def list_services(self):
"List all services"
return self.catalog.services
ret = {svc.name: svc.list_cmds() for svc in self.catalog.services.values()}
return ret
def shell_enable(self, **kwargs):
"Enable shell"
return self.catalog.shell_enable(**kwargs)
def shell_disable(self, **kwargs):
"Disable shell"
return self.catalog.shell_disable(**kwargs)
def shell_install(self, shell="bash", completion=True):
"Show your install script for shell"
assets_dir = os.path.join(get_root_pkg_dir("iam"), "assets")
# Shell selector
if shell.endswith("bash"):
config_file = ".bashrc"
init_file = os.path.join(assets_dir, "init_bash.sh")
completion_file = os.path.join(assets_dir, "iam_completion.sh")
else:
raise Exception(f"Unsupported shell: {shell}")
# TOFIX: Implement for other shells ?
out = []
out.append(f"### IAM ###")
out.append(f"if command -v iam &>/dev/null; then")
out.append(f" source {init_file}")
if completion:
out.append(f" source {completion_file}")
out.append(f"fi")
out.append(f"### IAM ###")
return "\n".join(out)