229 lines
6.4 KiB
Python
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)
|