update: plugin system to load yaml/python
This commit is contained in:
parent
cbe88fba59
commit
724eb8e780
87
iam/app.py
87
iam/app.py
@ -1,6 +1,7 @@
|
||||
import glob
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from collections import namedtuple
|
||||
from copy import copy
|
||||
from pprint import pprint
|
||||
@ -12,23 +13,24 @@ from . import exceptions as error
|
||||
from .catalog import Catalog
|
||||
from .framework import DictItem, scoped_ident
|
||||
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 .providers import Providers
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
IamException = error.IamException
|
||||
# IamException = error.IamException
|
||||
|
||||
|
||||
class App:
|
||||
name = NAME
|
||||
|
||||
def __init__(self, ident="_", config_path=None):
|
||||
# Load application
|
||||
db = self.load_config_files(config_path=config_path)
|
||||
def __init__(self, ident="_", config_path=None, cli_context=None):
|
||||
self.cli_ctx = cli_context
|
||||
|
||||
self.load_db(db)
|
||||
# Load application
|
||||
self.init_db(config_path)
|
||||
self.init_ident(ident)
|
||||
|
||||
def init_ident(self, ident):
|
||||
@ -49,7 +51,7 @@ class App:
|
||||
self.ident = self.idents.get(self.ident_name)
|
||||
|
||||
# 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):
|
||||
"Fetch config from default place"
|
||||
@ -68,7 +70,7 @@ class App:
|
||||
files = glob.glob(f"{path}/*.yml")
|
||||
|
||||
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_)
|
||||
for payload in payloads:
|
||||
config_candidates.append(
|
||||
@ -89,16 +91,24 @@ class App:
|
||||
|
||||
return config
|
||||
|
||||
def load_db(self, db):
|
||||
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)
|
||||
|
||||
@ -114,29 +124,52 @@ class App:
|
||||
|
||||
plugins = {}
|
||||
for plugin_name in plugins_refs:
|
||||
if not ":" in plugin_name:
|
||||
plugin_name = plugin_name + ":all"
|
||||
providers = self.parse_plugin_def(plugin_name)
|
||||
|
||||
load_one = False
|
||||
if not plugin_name.endswith(":all"):
|
||||
load_one = plugin_name.split(":", 2)[1]
|
||||
plugins.update(providers)
|
||||
|
||||
mod = None
|
||||
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)
|
||||
# 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"
|
||||
|
||||
|
||||
101
iam/catalog.py
101
iam/catalog.py
@ -11,8 +11,8 @@ import sh
|
||||
from . import exceptions as error
|
||||
from .framework import DictCtrl, DictItem, KeyValue, KeyValueExtra
|
||||
from .idents import Ident
|
||||
from .lib.utils import (empty, format_render, jinja_render,
|
||||
jinja_template_vars, uniq)
|
||||
from .lib.utils import (duplicates, empty, format_render, import_module,
|
||||
jinja_render, jinja_template_vars, uniq)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
cli_logger = logging.getLogger("iam.cli")
|
||||
@ -55,6 +55,7 @@ class ServiceCommand(DictItem):
|
||||
default_attrs = {
|
||||
"desc": "Service without description",
|
||||
"cmd": "", # Direct commands to launch
|
||||
"python": "", # Will be run in as python
|
||||
"shell": "", # Will be run in a shell (or specific shell below)
|
||||
# "shell_sh": "",
|
||||
# "shell_bash": "",
|
||||
@ -301,7 +302,7 @@ class Services(DictCtrl):
|
||||
|
||||
# Retrieve command list
|
||||
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
|
||||
cmd_split_idx = None
|
||||
@ -328,26 +329,28 @@ class Services(DictCtrl):
|
||||
def list_cmds(self):
|
||||
"List all services commands"
|
||||
|
||||
ret = []
|
||||
cmd_catalog = []
|
||||
for name, service in self.items():
|
||||
cmds = service.get_cmds()
|
||||
ret.extend(cmds)
|
||||
|
||||
# Check for invalid configs
|
||||
conf = []
|
||||
for cmd in ret:
|
||||
prefix = cmd.name.split(" ")[0]
|
||||
if prefix in self.RESERVED_CMD_PREFIX:
|
||||
_msg = f"Forbidden prefix! {cmd}"
|
||||
raise Exception(_msg)
|
||||
conf.append(cmd.name)
|
||||
dump = uniq(conf)
|
||||
for cmd in cmds:
|
||||
# Check for invalid prefixes
|
||||
prefix = cmd.name.split(" ")[0]
|
||||
if prefix in self.RESERVED_CMD_PREFIX:
|
||||
_msg = f"Forbidden prefix for command '{cmd.name}' in service: '{service.name}'"
|
||||
raise error.ConfigForbiddenPrefixCmds(_msg)
|
||||
|
||||
if conf != dump:
|
||||
_msg = f"Duplicates values! {conf}"
|
||||
raise Exception(_msg)
|
||||
# Check for duplicates
|
||||
dups = [x for x in cmd_catalog if x.cmd.name == cmd.name]
|
||||
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):
|
||||
"""
|
||||
@ -763,6 +766,7 @@ class Context(DictItem):
|
||||
|
||||
default_attrs = {
|
||||
"ident": None,
|
||||
"cli_ctx": {},
|
||||
}
|
||||
|
||||
# Overrides
|
||||
@ -795,7 +799,7 @@ class Context(DictItem):
|
||||
class Catalog:
|
||||
"Manage catalog resources"
|
||||
|
||||
def __init__(self, app, ident):
|
||||
def __init__(self, app, ident, cli_ctx=None):
|
||||
# Prepare catalog
|
||||
self.app = app
|
||||
self.ident = ident
|
||||
@ -803,6 +807,7 @@ class Catalog:
|
||||
# Prepare context
|
||||
ctx_conf = {
|
||||
"ident": ident,
|
||||
"cli_ctx": cli_ctx,
|
||||
}
|
||||
self.context = Context("current_context", ctx_conf, parent=self)
|
||||
|
||||
@ -847,9 +852,11 @@ class Catalog:
|
||||
# Get resources from providers and user
|
||||
providers = self.app.providers
|
||||
ident = self.ident
|
||||
# pprint (self.__dict__)
|
||||
|
||||
# Build kind catalog
|
||||
res_kind_configs = providers.get_resource_kinds()
|
||||
assert res_kind_configs, "TOFIX"
|
||||
self.resources_kind = ResourcesKinds(
|
||||
"CatalogResourcesKinds", payload=res_kind_configs, parent=self
|
||||
)
|
||||
@ -986,12 +993,13 @@ class Catalog:
|
||||
def run_svc_cmd(self, cmd):
|
||||
"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)
|
||||
|
||||
service = cmd.service
|
||||
service = cmd_def.svc
|
||||
cmd = cmd_def.cmd
|
||||
|
||||
# pprint (service)
|
||||
# pprint (service.__dict__)
|
||||
@ -1019,15 +1027,17 @@ class Catalog:
|
||||
|
||||
# pprint (ctx_vars)
|
||||
|
||||
env_prefix = "IAM_"
|
||||
new_env = dict(os.environ)
|
||||
new_env.update(ctx_vars)
|
||||
# pprint(new_env)
|
||||
new_env.update({env_prefix + key: val for key, val in ctx_vars.items()})
|
||||
|
||||
if cmd.cmd and cmd.shell:
|
||||
logger.warning(f"Duplicate cmd and shell for {service.name}")
|
||||
# Validate config: TOFIX: Should not be here !
|
||||
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
|
||||
|
||||
if cmd.shell:
|
||||
mode = "shell"
|
||||
payload = cmd.shell
|
||||
@ -1069,9 +1079,12 @@ class Catalog:
|
||||
payload = cmd.cmd
|
||||
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_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)
|
||||
|
||||
@ -1080,11 +1093,41 @@ class Catalog:
|
||||
|
||||
cmd = sh.Command(sh_cmd)
|
||||
if sh_args:
|
||||
# sh_args = sh_args.split(" ")
|
||||
pprint(cmd)
|
||||
pprint(sh_args)
|
||||
out = cmd(sh_args, _fg=True, _env=new_env)
|
||||
else:
|
||||
out = cmd(_fg=True, _env=new_env)
|
||||
|
||||
# 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:
|
||||
raise Exception("MIssing cmd or shell in config !")
|
||||
|
||||
@ -4,6 +4,7 @@ import io
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
from enum import Enum
|
||||
from pprint import pprint
|
||||
from types import SimpleNamespace
|
||||
@ -45,7 +46,7 @@ else:
|
||||
import rich_click as click
|
||||
from rich_click import RichGroup
|
||||
|
||||
from rich import box # , print
|
||||
from rich import box, print
|
||||
from rich.console import Console
|
||||
# Overrides defaults
|
||||
from rich.pretty import pprint
|
||||
@ -90,7 +91,8 @@ list_view = ViewList(output=console.print)
|
||||
APP_NAME = "iam"
|
||||
APP_VERSION = "__version__TOFIX"
|
||||
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
|
||||
DEFAULT_LOG_LEVEL = 30 # warning
|
||||
@ -129,16 +131,37 @@ def global_options(function):
|
||||
# is_flag=True, show_default=False, default=False,
|
||||
# help="Force"
|
||||
# )(function)
|
||||
# function = click.option(
|
||||
# "interactive", "--interactive", "-i"
|
||||
# is_flag=True, show_default=False, default=False,
|
||||
# help="Interactive mode"
|
||||
# )(function)
|
||||
|
||||
function = click.option(
|
||||
"interactive",
|
||||
"--interactive/--no-interactive",
|
||||
"-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(
|
||||
# "recursive", "--recursive", "-r"
|
||||
# is_flag=True, show_default=False, default=False,
|
||||
# help="Recursive mode"
|
||||
# )(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(
|
||||
"quiet",
|
||||
@ -171,12 +194,6 @@ def global_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
|
||||
function = click.option(
|
||||
"-v",
|
||||
@ -243,6 +260,8 @@ def cli_app(
|
||||
config: str = DEFAULT_IDENT,
|
||||
ident: str = DEFAULT_IDENT,
|
||||
quiet=False,
|
||||
interactive=False,
|
||||
colors=False,
|
||||
log_trace=False,
|
||||
fmt_fields=None,
|
||||
fmt_sort=None,
|
||||
@ -255,6 +274,15 @@ def cli_app(
|
||||
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
|
||||
log_level = DEFAULT_LOG_LEVEL - verbose * 10
|
||||
log_level = log_level if log_level > 0 else 10
|
||||
@ -272,6 +300,8 @@ def cli_app(
|
||||
log_level=log_level,
|
||||
fmt_name=fmt_name or DEFAULT_FORMAT,
|
||||
ident=ident,
|
||||
interactive=interactive,
|
||||
quiet=quiet,
|
||||
)
|
||||
render_config = {
|
||||
"fmt_name": cli_config.fmt_name,
|
||||
@ -288,7 +318,7 @@ def cli_app(
|
||||
# Instanciate app
|
||||
logger.info(f"Current ident: {ident}")
|
||||
ctx.obj = SimpleNamespace(
|
||||
app=App(config_path=config, ident=ident),
|
||||
app=App(config_path=config, ident=ident, cli_context=cli_config),
|
||||
cli=cli_config,
|
||||
render_item=render_item,
|
||||
render_list=render_list,
|
||||
@ -892,12 +922,13 @@ def cli_app_run(ctx, cmd_params):
|
||||
|
||||
if show_list:
|
||||
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
|
||||
ctx.obj.render_list(
|
||||
data,
|
||||
["name", "desc"],
|
||||
["name", "desc", "service"],
|
||||
conf={
|
||||
"table_title": f"Services commands",
|
||||
# "fmt_name": fmt_name,
|
||||
@ -969,11 +1000,18 @@ def run():
|
||||
clean_terminate(err)
|
||||
|
||||
# Developper catchall
|
||||
raise Exception(err) from err
|
||||
# logger.error(traceback.format_exc())
|
||||
# logger.critical("Uncatched error %s; this may be a bug!", err.__class__)
|
||||
# logger.critical("Exit 1 with bugs")
|
||||
# sys.exit(1)
|
||||
if APP_COLORS:
|
||||
console.print_exception(show_locals=True)
|
||||
else:
|
||||
# raise Exception(err) from err
|
||||
|
||||
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__":
|
||||
|
||||
@ -2,6 +2,10 @@ class IamException(Exception):
|
||||
"Iam Exception"
|
||||
|
||||
|
||||
# Second level classes
|
||||
# =====================
|
||||
|
||||
|
||||
class UnresolvedResourceDependencies(IamException):
|
||||
"Raised when resources dependency is unmet"
|
||||
|
||||
@ -20,3 +24,23 @@ class MissingConfigFiles(IamException):
|
||||
|
||||
class UnknownServiceCommand(IamException):
|
||||
"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"
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import importlib
|
||||
import importlib.util
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
@ -72,6 +73,21 @@ def empty(item):
|
||||
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):
|
||||
"Prune empty lists, dicts and None values from list or dict"
|
||||
|
||||
@ -197,6 +213,16 @@ def to_hcl(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):
|
||||
"Render to toml"
|
||||
pprint(data)
|
||||
@ -274,6 +300,18 @@ def get_pkg_dir(name):
|
||||
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):
|
||||
"Simple helper to load dynamically python modules"
|
||||
return importlib.import_module(package)
|
||||
|
||||
@ -1,7 +1,21 @@
|
||||
plugin_base = {
|
||||
"resources_def": {
|
||||
# Secrets
|
||||
# Main
|
||||
# -------------
|
||||
"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": {
|
||||
"desc": "Environment secret" "vars",
|
||||
"input": {"secret_env": None},
|
||||
@ -11,7 +25,6 @@ plugin_base = {
|
||||
"input": {"secret_file": None},
|
||||
},
|
||||
# Auths
|
||||
"auth": {"desc": "Authentification"},
|
||||
"auth.password": {"desc": "Password", "input": {"password": None}},
|
||||
"auth.token": {
|
||||
"desc": "Token",
|
||||
@ -22,20 +35,15 @@ plugin_base = {
|
||||
"input": {"token": None},
|
||||
},
|
||||
# Accounts
|
||||
"account": {
|
||||
"desc": "Account",
|
||||
"input": {"password": None, "user": None},
|
||||
},
|
||||
"account.email": {"desc": "Email account", "input": {"email": None}},
|
||||
# Services
|
||||
"service": {"desc": "Session service"},
|
||||
# ID
|
||||
"service.id": {"desc": "Default ident service"},
|
||||
},
|
||||
"resources": {
|
||||
"service.id": {
|
||||
"enabled": True,
|
||||
},
|
||||
"service.scope": {
|
||||
"enabled": True,
|
||||
},
|
||||
},
|
||||
"services": {
|
||||
"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}
|
||||
|
||||
@ -2,9 +2,9 @@ import os
|
||||
|
||||
from iam.lib.utils import get_pkg_dir, open_yaml, to_yaml
|
||||
|
||||
all = None
|
||||
providers = None
|
||||
yml_dir = get_pkg_dir(__name__)
|
||||
conf_files = open_yaml(os.path.join(yml_dir, "devops.yml"))
|
||||
for conf in conf_files:
|
||||
all = conf.get("providers", {})
|
||||
providers = conf.get("providers", {})
|
||||
break
|
||||
|
||||
@ -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", {})
|
||||
@ -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
|
||||
Loading…
x
Reference in New Issue
Block a user