update: plugin system to load yaml/python

This commit is contained in:
mrjk 2023-10-11 03:18:30 -04:00
parent cbe88fba59
commit 724eb8e780
9 changed files with 295 additions and 504 deletions

View File

@ -1,6 +1,7 @@
import glob import glob
import logging import logging
import os import os
import sys
from collections import namedtuple from collections import namedtuple
from copy import copy from copy import copy
from pprint import pprint from pprint import pprint
@ -12,23 +13,24 @@ from . import exceptions as error
from .catalog import Catalog from .catalog import Catalog
from .framework import DictItem, scoped_ident from .framework import DictItem, scoped_ident
from .idents import Idents from .idents import Idents
from .lib.utils import get_root_pkg_dir, import_module, open_yaml from .lib.utils import (filter_existing_files, get_root_pkg_dir, import_module,
open_yaml, to_yaml)
from .meta import NAME, VERSION from .meta import NAME, VERSION
from .providers import Providers from .providers import Providers
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
IamException = error.IamException # IamException = error.IamException
class App: class App:
name = NAME name = NAME
def __init__(self, ident="_", config_path=None): def __init__(self, ident="_", config_path=None, cli_context=None):
# Load application self.cli_ctx = cli_context
db = self.load_config_files(config_path=config_path)
self.load_db(db) # Load application
self.init_db(config_path)
self.init_ident(ident) self.init_ident(ident)
def init_ident(self, ident): def init_ident(self, ident):
@ -49,7 +51,7 @@ class App:
self.ident = self.idents.get(self.ident_name) self.ident = self.idents.get(self.ident_name)
# Load user and catalog # Load user and catalog
self.catalog = Catalog(self, self.ident) self.catalog = Catalog(self, self.ident, self.cli_ctx)
def load_config_files(self, config_path=None): def load_config_files(self, config_path=None):
"Fetch config from default place" "Fetch config from default place"
@ -68,7 +70,7 @@ class App:
files = glob.glob(f"{path}/*.yml") files = glob.glob(f"{path}/*.yml")
for file_ in files: for file_ in files:
file_ = os.path.join(path, file_) # file_ = os.path.join(path, file_) # Python > 3.10, see above
payloads = open_yaml(file_) payloads = open_yaml(file_)
for payload in payloads: for payload in payloads:
config_candidates.append( config_candidates.append(
@ -89,16 +91,24 @@ class App:
return config return config
def load_db(self, db): def init_db(self, config_path):
"Load database" "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 # Init Databases
db = self.load_config_files(config_path=config_path)
self.db = db self.db = db
self._load_plugins(db.get("plugins", [])) self._load_plugins(db.get("plugins", []))
# Load providers # Load providers
providers_conf = {} providers_conf = {}
providers_conf.update(self.plugin_providers) providers_conf.update(self.plugin_providers)
# pprint(providers_conf)
providers_conf.update(db.get("providers", {})) providers_conf.update(db.get("providers", {}))
self.providers = Providers("ConfigProviders", providers_conf) self.providers = Providers("ConfigProviders", providers_conf)
@ -114,29 +124,52 @@ class App:
plugins = {} plugins = {}
for plugin_name in plugins_refs: for plugin_name in plugins_refs:
if not ":" in plugin_name: providers = self.parse_plugin_def(plugin_name)
plugin_name = plugin_name + ":all"
load_one = False plugins.update(providers)
if not plugin_name.endswith(":all"):
load_one = plugin_name.split(":", 2)[1]
mod = None # print (to_yaml(mod))
try:
mod = import_module(plugin_name)
except ModuleNotFoundError as err:
msg = f"Impossible to load module: {plugin_name}"
raise IamException(msg)
print(err)
continue
if load_one:
mod = {load_one: mod}
plugins.update(mod)
self.plugin_providers = plugins 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): def user_dump(self, pattern=None):
"Dump ident config" "Dump ident config"

View File

@ -11,8 +11,8 @@ import sh
from . import exceptions as error from . import exceptions as error
from .framework import DictCtrl, DictItem, KeyValue, KeyValueExtra from .framework import DictCtrl, DictItem, KeyValue, KeyValueExtra
from .idents import Ident from .idents import Ident
from .lib.utils import (empty, format_render, jinja_render, from .lib.utils import (duplicates, empty, format_render, import_module,
jinja_template_vars, uniq) jinja_render, jinja_template_vars, uniq)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
cli_logger = logging.getLogger("iam.cli") cli_logger = logging.getLogger("iam.cli")
@ -55,6 +55,7 @@ class ServiceCommand(DictItem):
default_attrs = { default_attrs = {
"desc": "Service without description", "desc": "Service without description",
"cmd": "", # Direct commands to launch "cmd": "", # Direct commands to launch
"python": "", # Will be run in as python
"shell": "", # Will be run in a shell (or specific shell below) "shell": "", # Will be run in a shell (or specific shell below)
# "shell_sh": "", # "shell_sh": "",
# "shell_bash": "", # "shell_bash": "",
@ -301,7 +302,7 @@ class Services(DictCtrl):
# Retrieve command list # Retrieve command list
cmds = self.list_cmds() cmds = self.list_cmds()
cmd_names = [cmd.name for cmd in cmds] cmd_names = [cmd.cmd.name for cmd in cmds]
# Find best matching command # Find best matching command
cmd_split_idx = None cmd_split_idx = None
@ -328,26 +329,28 @@ class Services(DictCtrl):
def list_cmds(self): def list_cmds(self):
"List all services commands" "List all services commands"
ret = [] cmd_catalog = []
for name, service in self.items(): for name, service in self.items():
cmds = service.get_cmds() cmds = service.get_cmds()
ret.extend(cmds)
# Check for invalid configs for cmd in cmds:
conf = [] # Check for invalid prefixes
for cmd in ret:
prefix = cmd.name.split(" ")[0] prefix = cmd.name.split(" ")[0]
if prefix in self.RESERVED_CMD_PREFIX: if prefix in self.RESERVED_CMD_PREFIX:
_msg = f"Forbidden prefix! {cmd}" _msg = f"Forbidden prefix for command '{cmd.name}' in service: '{service.name}'"
raise Exception(_msg) raise error.ConfigForbiddenPrefixCmds(_msg)
conf.append(cmd.name)
dump = uniq(conf)
if conf != dump: # Check for duplicates
_msg = f"Duplicates values! {conf}" dups = [x for x in cmd_catalog if x.cmd.name == cmd.name]
raise Exception(_msg) if dups:
svcs = ",".join([x.svc.name for x in dups])
_msg = f"Duplicate command name '{cmd.name}' for services: '{svcs}' and '{service.name}'"
raise error.ConfigDuplicateCmds(_msg)
return ret # Append to catalog
cmd_catalog.append(SimpleNamespace(svc=service, cmd=cmd))
return cmd_catalog
def get_linked_resources(self): def get_linked_resources(self):
""" """
@ -763,6 +766,7 @@ class Context(DictItem):
default_attrs = { default_attrs = {
"ident": None, "ident": None,
"cli_ctx": {},
} }
# Overrides # Overrides
@ -795,7 +799,7 @@ class Context(DictItem):
class Catalog: class Catalog:
"Manage catalog resources" "Manage catalog resources"
def __init__(self, app, ident): def __init__(self, app, ident, cli_ctx=None):
# Prepare catalog # Prepare catalog
self.app = app self.app = app
self.ident = ident self.ident = ident
@ -803,6 +807,7 @@ class Catalog:
# Prepare context # Prepare context
ctx_conf = { ctx_conf = {
"ident": ident, "ident": ident,
"cli_ctx": cli_ctx,
} }
self.context = Context("current_context", ctx_conf, parent=self) self.context = Context("current_context", ctx_conf, parent=self)
@ -847,9 +852,11 @@ class Catalog:
# Get resources from providers and user # Get resources from providers and user
providers = self.app.providers providers = self.app.providers
ident = self.ident ident = self.ident
# pprint (self.__dict__)
# Build kind catalog # Build kind catalog
res_kind_configs = providers.get_resource_kinds() res_kind_configs = providers.get_resource_kinds()
assert res_kind_configs, "TOFIX"
self.resources_kind = ResourcesKinds( self.resources_kind = ResourcesKinds(
"CatalogResourcesKinds", payload=res_kind_configs, parent=self "CatalogResourcesKinds", payload=res_kind_configs, parent=self
) )
@ -986,12 +993,13 @@ class Catalog:
def run_svc_cmd(self, cmd): def run_svc_cmd(self, cmd):
"Run a command against the services" "Run a command against the services"
cmd, args = self.services.get_svc_command(cmd) cmd_def, args = self.services.get_svc_command(cmd)
# pprint (cmd.cmd) # pprint (cmd_def)
# pprint (args) # pprint (args)
service = cmd.service service = cmd_def.svc
cmd = cmd_def.cmd
# pprint (service) # pprint (service)
# pprint (service.__dict__) # pprint (service.__dict__)
@ -1019,15 +1027,17 @@ class Catalog:
# pprint (ctx_vars) # pprint (ctx_vars)
env_prefix = "IAM_"
new_env = dict(os.environ) new_env = dict(os.environ)
new_env.update(ctx_vars) new_env.update({env_prefix + key: val for key, val in ctx_vars.items()})
# pprint(new_env)
if cmd.cmd and cmd.shell: # Validate config: TOFIX: Should not be here !
logger.warning(f"Duplicate cmd and shell for {service.name}") tests = [cmd.cmd, cmd.shell, cmd.python]
tests = [x for x in tests if x]
if len(tests) > 1:
logger.warning(f"Duplicate cmd and shell for {service.name}: {tests}")
out = None out = None
if cmd.shell: if cmd.shell:
mode = "shell" mode = "shell"
payload = cmd.shell payload = cmd.shell
@ -1069,9 +1079,12 @@ class Catalog:
payload = cmd.cmd payload = cmd.cmd
real_cmd = jinja_render(payload, ctx_vars) real_cmd = jinja_render(payload, ctx_vars)
cmd_parts = real_cmd.split(" ", 1) # print ("PAYLOAD", real_cmd)
cmd_parts = real_cmd.split(" ")
sh_cmd = cmd_parts[0] sh_cmd = cmd_parts[0]
sh_args = cmd_parts[1] if len(cmd_parts) > 0 else [] sh_args = cmd_parts[1:] if len(cmd_parts) > 1 else []
sh_args.extend(args)
# print ("Prepare", sh_cmd, " ||| ", sh_args) # print ("Prepare", sh_cmd, " ||| ", sh_args)
@ -1080,11 +1093,41 @@ class Catalog:
cmd = sh.Command(sh_cmd) cmd = sh.Command(sh_cmd)
if sh_args: if sh_args:
# sh_args = sh_args.split(" ")
pprint(cmd)
pprint(sh_args)
out = cmd(sh_args, _fg=True, _env=new_env) out = cmd(sh_args, _fg=True, _env=new_env)
else: else:
out = cmd(_fg=True, _env=new_env) out = cmd(_fg=True, _env=new_env)
# print (out) # print (out)
elif cmd.python:
mode = "python"
plugin_name = cmd.python
# print ("LOAD MODULE", plugin_name)
# this expect a callable, function or class
try:
mod = import_module(plugin_name)
except ModuleNotFoundError as err:
msg = f"Impossible to load module: {plugin_name}"
raise IamException(msg)
print(err)
cmd_name = cmd.name.replace(" ", "_")
# pprint (mod)
# print ("locals")
# pprint (locals())
# print ("RUN PLUGIN")
mod(
cmd_name=cmd_name,
env=new_env,
vars=ctx_vars,
args=args,
ctx=self.context,
)
else: else:
raise Exception("MIssing cmd or shell in config !") raise Exception("MIssing cmd or shell in config !")

View File

@ -4,6 +4,7 @@ import io
import logging import logging
import os import os
import sys import sys
import traceback
from enum import Enum from enum import Enum
from pprint import pprint from pprint import pprint
from types import SimpleNamespace from types import SimpleNamespace
@ -45,7 +46,7 @@ else:
import rich_click as click import rich_click as click
from rich_click import RichGroup from rich_click import RichGroup
from rich import box # , print from rich import box, print
from rich.console import Console from rich.console import Console
# Overrides defaults # Overrides defaults
from rich.pretty import pprint from rich.pretty import pprint
@ -90,7 +91,8 @@ list_view = ViewList(output=console.print)
APP_NAME = "iam" APP_NAME = "iam"
APP_VERSION = "__version__TOFIX" APP_VERSION = "__version__TOFIX"
APP_EXCEPTION = error.IamException APP_EXCEPTION = error.IamException
APP_TTY = os.isatty(0)
APP_COLORS = APP_TTY or APP_RICH
# See: https://docs.python.org/3.8/library/logging.html#logging-levels # See: https://docs.python.org/3.8/library/logging.html#logging-levels
DEFAULT_LOG_LEVEL = 30 # warning DEFAULT_LOG_LEVEL = 30 # warning
@ -129,16 +131,37 @@ def global_options(function):
# is_flag=True, show_default=False, default=False, # is_flag=True, show_default=False, default=False,
# help="Force" # help="Force"
# )(function) # )(function)
# function = click.option(
# "interactive", "--interactive", "-i" function = click.option(
# is_flag=True, show_default=False, default=False, "interactive",
# help="Interactive mode" "--interactive/--no-interactive",
# )(function) "-I",
is_flag=True,
show_default=APP_TTY,
default=APP_TTY,
help="Interactive mode",
)(function)
function = click.option(
"colors",
"--colors/--no-colors",
"-C",
is_flag=True,
show_default=APP_COLORS,
default=APP_COLORS,
help="Enable colors",
)(function)
# function = click.option( # function = click.option(
# "recursive", "--recursive", "-r" # "recursive", "--recursive", "-r"
# is_flag=True, show_default=False, default=False, # is_flag=True, show_default=False, default=False,
# help="Recursive mode" # help="Recursive mode"
# )(function) # )(function)
# function = click.option(
# 'dry_run', '-n', '--dry-run',
# is_flag=True, show_default=False, default=False,
# help="Dry run, do not take any actions."
# )(function)
function = click.option( function = click.option(
"quiet", "quiet",
@ -171,12 +194,6 @@ def global_options(function):
def format_options(function): def format_options(function):
# function = click.option(
# 'dry_run', '-n', '--dry-run',
# is_flag=True, show_default=False, default=False,
# help="Dry run, do not take any actions."
# )(function)
# Duplicates # Duplicates
function = click.option( function = click.option(
"-v", "-v",
@ -243,6 +260,8 @@ def cli_app(
config: str = DEFAULT_IDENT, config: str = DEFAULT_IDENT,
ident: str = DEFAULT_IDENT, ident: str = DEFAULT_IDENT,
quiet=False, quiet=False,
interactive=False,
colors=False,
log_trace=False, log_trace=False,
fmt_fields=None, fmt_fields=None,
fmt_sort=None, fmt_sort=None,
@ -255,6 +274,15 @@ def cli_app(
is not all that interesting. is not all that interesting.
""" """
# Show trace on errors, more like the --trace flag
if log_trace:
global APP_EXCEPTION
APP_EXCEPTION = None
# Disable COLORS
global APP_COLORS
APP_COLORS = colors
# Calculate log level # Calculate log level
log_level = DEFAULT_LOG_LEVEL - verbose * 10 log_level = DEFAULT_LOG_LEVEL - verbose * 10
log_level = log_level if log_level > 0 else 10 log_level = log_level if log_level > 0 else 10
@ -272,6 +300,8 @@ def cli_app(
log_level=log_level, log_level=log_level,
fmt_name=fmt_name or DEFAULT_FORMAT, fmt_name=fmt_name or DEFAULT_FORMAT,
ident=ident, ident=ident,
interactive=interactive,
quiet=quiet,
) )
render_config = { render_config = {
"fmt_name": cli_config.fmt_name, "fmt_name": cli_config.fmt_name,
@ -288,7 +318,7 @@ def cli_app(
# Instanciate app # Instanciate app
logger.info(f"Current ident: {ident}") logger.info(f"Current ident: {ident}")
ctx.obj = SimpleNamespace( ctx.obj = SimpleNamespace(
app=App(config_path=config, ident=ident), app=App(config_path=config, ident=ident, cli_context=cli_config),
cli=cli_config, cli=cli_config,
render_item=render_item, render_item=render_item,
render_list=render_list, render_list=render_list,
@ -892,12 +922,13 @@ def cli_app_run(ctx, cmd_params):
if show_list: if show_list:
ret = app.catalog.services.list_cmds() ret = app.catalog.services.list_cmds()
data = [[x.name, x.desc] for x in ret] pprint(ret)
data = [[x.cmd.name, x.cmd.desc, x.svc.name] for x in ret]
# Render data # Render data
ctx.obj.render_list( ctx.obj.render_list(
data, data,
["name", "desc"], ["name", "desc", "service"],
conf={ conf={
"table_title": f"Services commands", "table_title": f"Services commands",
# "fmt_name": fmt_name, # "fmt_name": fmt_name,
@ -969,11 +1000,18 @@ def run():
clean_terminate(err) clean_terminate(err)
# Developper catchall # Developper catchall
raise Exception(err) from err if APP_COLORS:
# logger.error(traceback.format_exc()) console.print_exception(show_locals=True)
# logger.critical("Uncatched error %s; this may be a bug!", err.__class__) else:
# logger.critical("Exit 1 with bugs") # raise Exception(err) from err
# sys.exit(1)
logger.error(traceback.format_exc())
if APP_EXCEPTION:
logger.critical("Uncatched error %s; this may be a bug!", err.__class__)
logger.critical("Exit 1 with bugs")
else:
logger.critical("Exited with %s error: %s", err.__class__.__name__, err)
sys.exit(1)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -2,6 +2,10 @@ class IamException(Exception):
"Iam Exception" "Iam Exception"
# Second level classes
# =====================
class UnresolvedResourceDependencies(IamException): class UnresolvedResourceDependencies(IamException):
"Raised when resources dependency is unmet" "Raised when resources dependency is unmet"
@ -20,3 +24,23 @@ class MissingConfigFiles(IamException):
class UnknownServiceCommand(IamException): class UnknownServiceCommand(IamException):
"Raised when a command is not matched against services" "Raised when a command is not matched against services"
# Configuration errors
# =====================
class ConfigurationError(IamException):
"Raised when a command is not matched against services"
class ConfigDuplicateCmds(ConfigurationError):
"Raised when two service commands overlap. May be caused by plugins"
class ConfigForbiddenPrefixCmds(ConfigurationError):
"Raised when a service is not correctly prefixed. May be caused by plugins"
class ConfigUnknownPlugin(ConfigurationError):
"Raised when a plugin can't be found"

View File

@ -1,4 +1,5 @@
import importlib import importlib
import importlib.util
import json import json
import logging import logging
import os import os
@ -72,6 +73,21 @@ def empty(item):
return False return False
def duplicates(_list):
"""Check if given list contains duplicates"""
known = set()
dup = set()
for item in _list:
if item in known:
dup.add(item)
else:
known.add(item)
if len(dup) > 0:
return list(dup)
return []
def prune(items): def prune(items):
"Prune empty lists, dicts and None values from list or dict" "Prune empty lists, dicts and None values from list or dict"
@ -197,6 +213,16 @@ def to_hcl(data):
# return tomllib.dumps(data) # return tomllib.dumps(data)
def filter_existing_files(root_path, candidates):
"""Return only existing files"""
result = [
os.path.join(root_path, cand)
for cand in candidates
if os.path.isfile(os.path.join(root_path, cand))
]
return list(set(result))
def to_toml(data): def to_toml(data):
"Render to toml" "Render to toml"
pprint(data) pprint(data)
@ -274,6 +300,18 @@ def get_pkg_dir(name):
return os.path.dirname(mod.__file__) return os.path.dirname(mod.__file__)
def import_module_file(package, path):
"Simple helper to load dynamically python modules"
spec = importlib.util.spec_from_file_location(package, path)
mod = importlib.util.module_from_spec(spec)
sys.modules[package] = mod
spec.loader.exec_module(mod)
return mod
def import_module_pkg(package): def import_module_pkg(package):
"Simple helper to load dynamically python modules" "Simple helper to load dynamically python modules"
return importlib.import_module(package) return importlib.import_module(package)

View File

@ -1,7 +1,21 @@
plugin_base = { plugin_base = {
"resources_def": { "resources_def": {
# Secrets # Main
# -------------
"secret": {"desc": "Secret", "input": {"secret": None}}, "secret": {"desc": "Secret", "input": {"secret": None}},
"auth": {"desc": "Authentification"},
"account": {
"desc": "Account",
"input": {"password": None, "user": None},
},
"service": {"desc": "Session service"},
"scope": {"desc": "Scoped identity"},
# Bases
"service.id": {"desc": "Default ident service"},
"service.scope": {"desc": "Default scope service"},
# Other assets:
# -------------
# Secrets
"secret.env": { "secret.env": {
"desc": "Environment secret" "vars", "desc": "Environment secret" "vars",
"input": {"secret_env": None}, "input": {"secret_env": None},
@ -11,7 +25,6 @@ plugin_base = {
"input": {"secret_file": None}, "input": {"secret_file": None},
}, },
# Auths # Auths
"auth": {"desc": "Authentification"},
"auth.password": {"desc": "Password", "input": {"password": None}}, "auth.password": {"desc": "Password", "input": {"password": None}},
"auth.token": { "auth.token": {
"desc": "Token", "desc": "Token",
@ -22,20 +35,15 @@ plugin_base = {
"input": {"token": None}, "input": {"token": None},
}, },
# Accounts # Accounts
"account": {
"desc": "Account",
"input": {"password": None, "user": None},
},
"account.email": {"desc": "Email account", "input": {"email": None}}, "account.email": {"desc": "Email account", "input": {"email": None}},
# Services
"service": {"desc": "Session service"},
# ID
"service.id": {"desc": "Default ident service"},
}, },
"resources": { "resources": {
"service.id": { "service.id": {
"enabled": True, "enabled": True,
}, },
"service.scope": {
"enabled": True,
},
}, },
"services": { "services": {
"id": { "id": {
@ -59,8 +67,29 @@ plugin_base = {
}, },
}, },
}, },
"scope": {
"desc": "Local scope identity/sub identities",
"commands": {
"shell_enable": {
"desc": "Enable shell scope",
"shell": "export SHELL_SCOPE={{scope}}",
},
"shell_disable": {
"desc": "Disable shell scope",
"shell": "unset SHELL_SCOPE",
},
"scope new": {
"desc": "Create shell scope",
"shell": "add_scope {{ param }}",
},
"scope delete": {
"desc": "Delete shell scope",
"shell": "rm_scope {{ param }}",
},
},
},
}, },
} }
all = {"base": plugin_base} providers = {"base": plugin_base}

View File

@ -2,9 +2,9 @@ import os
from iam.lib.utils import get_pkg_dir, open_yaml, to_yaml from iam.lib.utils import get_pkg_dir, open_yaml, to_yaml
all = None providers = None
yml_dir = get_pkg_dir(__name__) yml_dir = get_pkg_dir(__name__)
conf_files = open_yaml(os.path.join(yml_dir, "devops.yml")) conf_files = open_yaml(os.path.join(yml_dir, "devops.yml"))
for conf in conf_files: for conf in conf_files:
all = conf.get("providers", {}) providers = conf.get("providers", {})
break break

View File

@ -1,10 +0,0 @@
import os
from pprint import pprint
from iam.lib.utils import get_pkg_dir, open_yaml, to_yaml
yml_dir = get_pkg_dir(__name__)
plugin_conf = open_yaml(os.path.join(yml_dir, "local.yml"))[0]
all = plugin_conf.get("providers", {})

View File

@ -1,404 +0,0 @@
providers:
# Provider: SSH
# ==================
ssh:
services:
local.ssh_key:
desc: Local ssh key
inputs:
ssh_key_secret: ""
ssh_key_alg: "ed25519"
commands:
ssh new:
desc: Create new SSH key
# shell: |
# echo "This is my shelll !!! $SHELL"
# env | grep jez
# cmd: |
# env
# # echo Create ssh key: {{ssh_key_alg}} for ident '{{ident}}' with pass: {{ssh_key_secret}}
shell: |
# env | sort
# echo
SSH_KEY_ALG={{ssh_key_alg}}
SSH_KEY_VERSION="$(date +'%Y%m%d')"
SSH_KEY_HOST="$(hostname -f)"
SSH_KEY_FILE=$HOME/.ssh/{{ident}}/{{user}}_${SSH_KEY_ALG}_${SSH_KEY_VERSION}
SSH_KEY_COMMENT={{user}}@${SSH_KEY_HOST}:${SSH_KEY_ALG}_${SSH_KEY_VERSION}
echo mkdir -p $HOME/.ssh/{{ident}}/
echo ssh-keygen -f "${SSH_KEY_FILE}" \
-t ed25519 -a 100 \
{% if ssh_key_secret %}-N "{{ssh_key_secret}}"{%endif%} \
-C "$SSH_KEY_COMMENT"
ssh delete:
desc: Delete existing SSH key
cmd: |
find $HOME/.ssh/{{ident}}/ -name "{{user}}_*"
resources_def:
auth.ssh_certificate:
desc: SSH Certificates
input:
ssh_cert_file: null
needs:
- auth.ssh_key
auth.ssh_key:
desc: SSH Keypair
input:
ssh_key_file: null
ssh_key_secret: null
needs:
- kind: auth.password
remap:
ssh_key_secret: passord
account.ssh:
desc: Unix account
input:
host: null
# service.local.ssh_key:
# desc: A local ssh key
# resources:
# service.local.ssh_agent:
# enabled: true
# service.local.ssh_agent_keys:
# enabled: true
# loop:
# - auth.ssh_key:{ident}/ed25519
# - auth.ssh_key:{ident}/rsa4096
# - auth.ssh_key:{ident}/rsa2048
# - auth.ssh_key:{ident}/rsa1024
# - auth.ssh_key:{ident}
# loop_limit: 3
# Provider: GPG Agent
# ==================
gpg_agent:
resources_def:
auth.gpg_key:
desc: GPG keypair
input:
gpg_key_file: null
gpg_key_secret: null
needs:
- kind: auth.password
remap:
gpg_key_secret: passord
# Provider: SSH Agent
# ==================
ssh_agent:
services:
local.ssh_agent:
desc: Local ssh-agent
input:
ssh_agent_socket_dir: /run/user/ssh-agent
ssh_agent_tmout: 7d
commands:
shell_start:
desc: Start ssh-agent
shell: |
socket=$HOME/.local/state/ssh-agent/{{user}}
start=true
running=2
if [[ ! -e "$socket.env" ]]; then
running=2
elif [[ -e "$socket" ]]; then
running=$(SSH_AUTH_SOCK=$socket ssh-add -l &>/dev/null; echo $rc)
fi
if [[ "$running" -eq 2 ]]; then
# Start agent
>&2 echo "Start ssh-agent for {{ident}}"
mkdir -p "${socket%/*}"
ssh-agent -a $socket -t {{ssh_agent_tmout}} > $socket.env
fi
unset socket start running
# if [[ -d "/run/user/$(id -u)" ]]; then
# socket=/run/user/$(id -u)/ssh-agent/{{user}}
# else
# fi
shell_enable:
desc: Enable ssh-agent
shell: |
socket=$HOME/.local/state/ssh-agent/{{user}}
if [[ -e "$socket.env" ]]; then
# >&2 echo "Enable ssh-agent for {{ident}}"
source "$socket.env" >/dev/null
fi
unset socket
shell_disable:
desc: Disable ssh-agent
shell: |
unset SSH_AUTH_SOCK SSH_AGENT_PID
shell_stop:
desc: Kill ssh-agent
shell: |
socket=$HOME/.local/state/ssh-agent/{{user}}
if [[ -e "$socket.env" ]]; then
# >&2 echo "Enable ssh-agent for {{ident}}"
source "$socket.env" >/dev/null
# fi
# if [[ -n "$SSH_AGENT_PID" ]]; then
>&2 echo "Kill ssh-agent for {{ident}}"
eval "(ssh-agent -k)" >/dev/null
[[ -e "$socket.env" ]] && rm "$socket.env" || true
fi
unset socket
# env_file="$HOME/.local/state/ssh-agent/{{user}}.env"
# if [[ -f "$env_file" ]]; then
# source "$env_file"
# fi
# if [[ -f "$env_file" ]]; then
# rm "$env_file"
# fi
local.ssh_agent_keys:
desc: Local ssh-agent keys
commands:
ssh add:
desc: Unload keys into ssh-agent
shell: ssh-agent -d {ssh_key_file}
ssh rm:
desc: Load keys into ssh-agent
shell: |
ssh-add {% for item in loop %} {{item.ssh_key_file}} {% endfor %}
required_services:
- local.ssh_agent
resources_def:
service.local.ssh_agent:
desc: Configure ssh-agent daemon
service.local.ssh_agent_keys:
desc: Configure ssh-agent keys autoloader
resources:
service.local.ssh_agent:
enabled: true
service.local.ssh_agent_keys:
enabled: true
loop:
- auth.ssh_key:{ident}/ed25519
- auth.ssh_key:{ident}/rsa4096
- auth.ssh_key:{ident}/rsa2048
- auth.ssh_key:{ident}/rsa1024
- auth.ssh_key:{ident}
loop_limit: 3
# Provider: Git Config
# ==================
git:
services:
local.git:
desc: Git identity
# input:
# ssh_agent_socket_dir: /run/user/ssh-agent
# ssh_agent_tmout: 7d
commands:
shell_enable:
desc: Enable git identity
shell: |
export GIT_AUTHOR_NAME='{{ident}}'
export GIT_AUTHOR_EMAIL='{{email}}'
export GIT_COMMITTER_NAME='{{ident}}'
export GIT_COMMITTER_EMAIL='{{email}}'
shell_disable:
desc: Disable git identity
shell: |
unset GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL
local.git_home:
desc: Home as git repo
input:
git_dir: "$HOME"
git_work_tree: $HOME/.local/share/home_git
commands:
shell_enable:
desc: Enable git home management
shell: |
export GIT_DIR="{{git_dir}}"
export GIT_WORK_TREE="{{git_work_tree}}/{{ ident }}"
shell_disable:
desc: Disable git home management
shell: |
unset GIT_DIR GIT_WORK_TREE
required_services:
- local.git
resources_def:
service.local.git:
desc: Configure git
service.local.git_home:
desc: Configure home as git repo
resources:
service.local.git:
enabled: true
uses:
- account:{user}
# Disabled by default
service.local.git_home:
# Provider: PS1 Config
# ==================
ps1:
services:
local.ps1:
desc: PS1 prompt
input:
enabled: True
commands:
shell_enable:
desc: Enable PS1
shell: |
export OLD_PS1=$PS1
export PS1="\033[0;34m\]({{ident}})\033[00m\] ${PS1}"
shell_disable:
desc: Disable PS1
shell: |
export PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
# export PS1="$OLD_PS1"
resources_def:
service.local.ps1:
desc: PS1 prompt
resources:
service.local.ps1:
desc: Custom Ident PS1
# EXISTING
# WARN__: Your workspace is already activated
# NOTICE: Enabling id ...
# export SHELL_ID='mrjk'
# export GIT_AUTHOR_NAME='mrjk'
# export GIT_AUTHOR_EMAIL='mrjk.78@gmail.com'
# export GIT_COMMITTER_NAME='mrjk'
# export GIT_COMMITTER_EMAIL='mrjk.78@gmail.com'
# NOTICE: Enabling gpg ...
# export GNUPGHOME=/home/jez/.config/gpg/mrjk
# export GPG_AGENT_INFO=/run/user/1000/pgp-agent/mrjk/socket
# export GPG_DEFAULT_ID=mrjk
# export GPG_TTY=/dev/pts/48
# export GNUPGHOME=/home/jez/.config/gpg/mrjk
# NOTICE: Enabling ssh ...
# export SSH_AUTH_SOCK=/run/user/1000/ssh-agent/mrjk/socket
# NOTICE: Enabling gh ...
# export GH_TOKEN="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"
# export GH_REPO="mrjk"
# NOTICE: Enabling gitea ...
# export GITEA_SERVER_URL="ad808bc88fa37bce5e3bb963f1420aa575194d30"
# export GITEA_LOGIN="mrjk@git.jeznet.org"
# NOTICE: Enabling ps1 ...
# export PS1="\[\](mrjk)\[\] ${IDM_SHELL_PS1}"
# NOTICE: Identity 'mrjk' is loaded