clean: code, add features, better cli

This commit is contained in:
mrjk 2023-10-09 03:46:53 -04:00
parent b8df31f036
commit eda4c8ffd9
13 changed files with 318 additions and 275 deletions

View File

@ -35,14 +35,6 @@ class App:
"Init application with ident" "Init application with ident"
self.ident_raw = ident self.ident_raw = ident
# # Split ident from scope
# scope = ""
# if "/" in ident:
# parts = ident.split("/", 2)
# ident = parts[0]
# scope = parts[1]
view = scoped_ident(ident) view = scoped_ident(ident)
# Save important vars # Save important vars
@ -69,8 +61,11 @@ class App:
else: else:
path = os.path.join(BaseDirectory.xdg_config_home, self.name) path = os.path.join(BaseDirectory.xdg_config_home, self.name)
self.config_dir = path
if os.path.isdir(path): if os.path.isdir(path):
files = glob.glob("*.yml", root_dir=path) # files = glob.glob("*.yml", root_dir=path) # Python > 3.10
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_)
@ -105,7 +100,6 @@ class App:
providers_conf = {} providers_conf = {}
providers_conf.update(self.plugin_providers) providers_conf.update(self.plugin_providers)
providers_conf.update(db.get("providers", {})) providers_conf.update(db.get("providers", {}))
# pprint (providers_conf)
self.providers = Providers("ConfigProviders", providers_conf) self.providers = Providers("ConfigProviders", providers_conf)
# Load idents # Load idents
@ -159,20 +153,6 @@ class App:
return ret return ret
# def get_resource(self, name):
# "Query and resolve a resource"
# return self.catalog.get_resource(name)
# # def get_service(self, name, resolve=False):
# # "Query and resolve a service"
# # return self.catalog.get_service(name, resolve=resolve)
# def list_resources(self):
# "List all resources"
# return self.catalog.resources
def list_services(self): def list_services(self):
"List all services" "List all services"
@ -190,17 +170,27 @@ class App:
return self.catalog.shell_disable(**kwargs) return self.catalog.shell_disable(**kwargs)
def shell_install(self, shell="bash"): def shell_install(self, shell="bash", completion=True):
"Install iam in shell" "Show your install script for shell"
assets_dir = os.path.join(get_root_pkg_dir("iam"), "assets") assets_dir = os.path.join(get_root_pkg_dir("iam"), "assets")
# Shell selector
if shell == "bash": if shell.endswith("bash"):
config_file = ".bashrc" config_file = ".bashrc"
template_file = os.path.join(assets_dir, "init_bash.sh") init_file = os.path.join(assets_dir, "init_bash.sh")
completion_file = os.path.join(assets_dir, "iam_completion.sh")
else: else:
raise Exception(f"Unsupported shell: {shell}") raise Exception(f"Unsupported shell: {shell}")
return f"source {template_file}" # 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)

View File

@ -0,0 +1,29 @@
_iam_completion() {
local IFS=$'\n'
local response
response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD _IAM_COMPLETE=bash_complete $1)
for completion in $response; do
IFS=',' read type value <<< "$completion"
if [[ $type == 'dir' ]]; then
COMPREPLY=()
compopt -o dirnames
elif [[ $type == 'file' ]]; then
COMPREPLY=()
compopt -o default
elif [[ $type == 'plain' ]]; then
COMPREPLY+=($value)
fi
done
return 0
}
_iam_completion_setup() {
complete -o nosort -F _iam_completion iam
}
_iam_completion_setup;

View File

@ -16,7 +16,7 @@
IAM_BIN=$(command -v iam) IAM_BIN=$(command -v iam)
IAM_IDENT= IAM_IDENT=
_usage () i_usage ()
{ {
echo "iam shell wrapper" echo "iam shell wrapper"
@ -156,7 +156,7 @@ i ()
#echo $IAM_BIN shell status $current_ident #echo $IAM_BIN shell status $current_ident
;; ;;
help) help)
_usage i_usage
;; ;;
esac esac

View File

@ -1,12 +1,18 @@
import os
import logging import logging
from collections import namedtuple from collections import namedtuple
from pprint import pprint from pprint import pprint
from types import SimpleNamespace from types import SimpleNamespace
import sh
# TO BE REMOVED !!!!
import click
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 format_render, jinja_render, uniq from .lib.utils import format_render, jinja_render, uniq, jinja_template_vars, empty
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
cli_logger = logging.getLogger("iam.cli") cli_logger = logging.getLogger("iam.cli")
@ -52,31 +58,38 @@ class ServiceCommand(DictItem):
"cmd": "", # Direct commands to launch "cmd": "", # Direct commands to launch
"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_bash": "",
"shell_zsh": "",
"shell_fish": "",
"shell_bash": "",
"source": False, # True for enable commands ! # "shell_sh": "",
# "shell_bash": "",
# "shell_zsh": "",
# "shell_fish": "",
# "shell_bash": "",
# "source_output": False, # True for shell commands by default !
} }
# Overrides # Overrides
# --------------- # ---------------
def payload_transform(self, name, kwargs=None): def init(self):
"Transform short form into long form" "Transform short form into long form"
self.service = self.parent self.service = self.parent
payload = self._payload
if not isinstance(payload, dict): # def payload_transform(self, name, kwargs=None):
payload = {"cmd": payload} # "Transform short form into long form"
self._payload = payload # self.service = self.parent
# payload = self._payload
# if not isinstance(payload, dict):
# payload = {"cmd": payload}
# self._payload = payload
def cmd_name(self): def cmd_name(self):
return self.name.replace("_", " ") return self.name.replace("_", " ")
class Service(DictItem): class Service(DictItem):
"Hold provider services" "Hold provider services"
@ -772,9 +785,16 @@ class Context(DictItem):
assert isinstance(self.ident, Ident), f"Gotr: {self.ident}" assert isinstance(self.ident, Ident), f"Gotr: {self.ident}"
def init(self): def init(self):
self.catalog = self.parent
root_dir = self.catalog.app.config_dir
self._vars = { self._vars = {
"ident": self.ident.name, "ident": self.ident.name,
"user": self.ident.name, "user": self.ident.name,
"config_dir": root_dir,
"scripts_dir": os.path.join(root_dir, "scripts"),
"bin_dir": os.path.join(root_dir, "bin"),
} }
def get_vars(self): def get_vars(self):
@ -790,16 +810,16 @@ class Context(DictItem):
class Catalog: class Catalog:
"Manage catalog resources" "Manage catalog resources"
def __init__(self, mgr, ident): def __init__(self, app, ident):
# Prepare catalog # Prepare catalog
self.mgr = mgr self.app = app
self.ident = ident self.ident = ident
# Prepare context # Prepare context
ctx_conf = { ctx_conf = {
"ident": ident, "ident": ident,
} }
self.context = Context("current_context", ctx_conf) self.context = Context("current_context", ctx_conf, parent=self)
self._prepare_catalog() self._prepare_catalog()
@ -807,7 +827,7 @@ class Catalog:
# ================ # ================
def _merge_inst_configs(self, merge=True): def _merge_inst_configs(self, merge=True):
plugins_confs = self.mgr.providers.get_resource_configs() plugins_confs = self.app.providers.get_resource_configs()
user_confs = self.ident.get_resource_configs() user_confs = self.ident.get_resource_configs()
final_config = {} final_config = {}
@ -840,7 +860,7 @@ class Catalog:
"Prepare catalog resources configs" "Prepare catalog resources configs"
# Get resources from providers and user # Get resources from providers and user
providers = self.mgr.providers providers = self.app.providers
ident = self.ident ident = self.ident
# Build kind catalog # Build kind catalog
@ -949,8 +969,8 @@ class Catalog:
continue continue
# Build loop with at least ONE item # Build loop with at least ONE item
cmd = command.cmd cmd_shell = command.shell
if not cmd: if not cmd_shell:
continue continue
# Create context var dict # Create context var dict
@ -972,7 +992,7 @@ class Catalog:
logger.debug(f"{log_action_name} service: {res.name}") logger.debug(f"{log_action_name} service: {res.name}")
# pprint (ctx_vars) # pprint (ctx_vars)
cmd = jinja_render(cmd, ctx_vars) cmd = jinja_render(cmd_shell, ctx_vars)
output_code.append(f"# Loading: {action_name} {res.name} ({service.name})") output_code.append(f"# Loading: {action_name} {res.name} ({service.name})")
output_code.append(f"# =====================") output_code.append(f"# =====================")
output_code.append(cmd) output_code.append(cmd)
@ -989,15 +1009,13 @@ class Catalog:
cmd, args = self.services.get_svc_command(cmd) cmd, args = self.services.get_svc_command(cmd)
# pprint (cmd.cmd) # pprint (cmd.cmd)
pprint (args) # pprint (args)
service = cmd.service service = cmd.service
pprint (service) # pprint (service)
pprint (service.__dict__) # pprint (service.__dict__)
# pprint (dir(service)) # pprint (dir(service))
res = service.get_linked_resource() res = service.get_linked_resource()
# pprint (res) # pprint (res)
@ -1020,25 +1038,82 @@ class Catalog:
# } # }
# ) # )
pprint (ctx_vars) # pprint (ctx_vars)
new_env = dict(os.environ)
new_env.update(ctx_vars)
# pprint(new_env)
if cmd.cmd and cmd.shell:
logger.warning(f"Duplicate cmd and shell for {service.name}")
out = None
if cmd.cmd:
if cmd.shell:
mode = "shell"
payload = cmd.shell
# Scan for missing vars !!!
tmp = jinja_template_vars(payload)
prompt_vars = []
for var_name in tmp:
if var_name not in ctx_vars:
prompt_vars.append(var_name)
else:
var_value = ctx_vars[var_name]
if empty(var_value):
prompt_vars.append(var_name)
# Ask for missing vars
if len(prompt_vars) > 0:
# print ("MISSING VARS")
# pprint (prompt_vars)
answered_items = {}
for missed in prompt_vars:
default = ctx_vars.get(missed, None)
result = click.prompt(f'Select value for {missed}', default=default)
answered_items[missed] = result
ctx_vars.update(answered_items)
real_cmd = jinja_render(payload, ctx_vars)
# print ("RUN SHELL")
builtins = sh.bash.bake("-c")
out = builtins(real_cmd, _fg=True, _env=new_env)
# print (out)
elif cmd.cmd:
mode = "cmd" mode = "cmd"
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)
sh_cmd = cmd_parts[0]
sh_args = cmd_parts[1] if len(cmd_parts) > 0 else []
elif cmd.shell: # print ("Prepare", sh_cmd, " ||| ", sh_args)
mode = "shell"
payload = cmd.shell
real_cmd = jinja_render(payload, ctx_vars)
# proc = sh.bake(sh_cmd)
# sh(sh_cmd, *sh_args, _fg=True)
cmd = sh.Command(sh_cmd)
if sh_args:
out = cmd(sh_args, _fg=True, _env=new_env)
else:
out = cmd( _fg=True, _env=new_env)
# print (out)
else: else:
raise Exception("MIssing cmd or shell in config !") raise Exception("MIssing cmd or shell in config !")
return out
print ("RUN CMD", mode, "\n\n\n====\n", real_cmd) # print ("RUN CMD", mode, "\n\n\n====\n", real_cmd)

View File

@ -100,6 +100,7 @@ DEBUG = os.environ.get("IAM_DEBUG", "False")
DEFAULT_IDENT = os.environ.get("IAM_IDENT", os.environ.get("SHELL_IDENT", "")) DEFAULT_IDENT = os.environ.get("IAM_IDENT", os.environ.get("SHELL_IDENT", ""))
DEFAULT_FORMAT = os.environ.get("IAM_FORMAT", "table") DEFAULT_FORMAT = os.environ.get("IAM_FORMAT", "table")
DEFAULT_HELP_OPTIONS=["-h", "--help"] DEFAULT_HELP_OPTIONS=["-h", "--help"]
DEFAULT_SHELL = os.environ.get("SHELL", "bash")
# list_view.formats_enum # list_view.formats_enum
@ -849,7 +850,7 @@ class TmpGroup(click.Group):
@click.pass_context @click.pass_context
def cli_app_run(ctx, cmd_params): def cli_app_run(ctx, cmd_params):
print("Will run cmd:", cmd_params) # print("Will run cmd:", cmd_params)
# Check first help parameter only # Check first help parameter only
show_help = False show_help = False
@ -891,7 +892,8 @@ def cli_app_run(ctx, cmd_params):
# Run the output # Run the output
ret = app.catalog.run_svc_cmd(cmd=cmd_params) ret = app.catalog.run_svc_cmd(cmd=cmd_params)
pprint (ret, expand_all=True) print (ret)
# pprint (ret, expand_all=True)
@ -910,11 +912,14 @@ def cli_app_run(ctx, cmd_params):
@cli__shell.command("install") @cli__shell.command("install")
@click.argument("shell",
required=False,
default=DEFAULT_SHELL)
@click.pass_context @click.pass_context
def shell_install(ctx, verbose=None): def shell_install(ctx, shell=None, verbose=None):
"Install iam in your shell" "Install iam in your shell"
ret = ctx.obj.app.shell_install() ret = ctx.obj.app.shell_install(shell=shell)
# print("-- %< --" * 8) # print("-- %< --" * 8)
print(ret) print(ret)

View File

@ -1,5 +1,9 @@
import logging
import click import click
logger = logging.getLogger(__name__)
# Click Plugins # Click Plugins
# =============================== # ===============================
@ -9,8 +13,53 @@ class NestedHelpGroup(click.Group):
"""This class provides a way to show all commands of all children """This class provides a way to show all commands of all children
instead of just the parent. Optionnaly accept to hides groups or not. instead of just the parent. Optionnaly accept to hides groups or not.
This class is aimed to serve simple resource based CLIs, à la [cliff](),
but with the click library.
This class provides:
* Recursive command listing
* Command aliasing as described in (documentation)[https://click.palletsprojects.com/en/8.1.x/advanced/#command-aliases]
* Rich support (TODO, more complete wrapper of click-rich)
* Basic views (TODO)
# Recursive command list
# =============
Recursive listing on help commands, this is helpful to
have an quick overview of all commands on small clis. It also provides
an option to hide groups if you don't use them, which may reduce the size of
the help message in case of many groups.
# Command shortcuts
# =============
Let's image a command line that provides:
```
myapp user show <USER>
myapp user state <USER>
myapp user disable <USER>
```
You could use as shortcuts:
```
mysapp u sh USER
mysapp u st USER
mysapp u sd USER
```
But `mysapp u s USER` would fail as it would not know if it have to redirect to
`show` or `state` command, as both starts with `s`
""" """
# For partial name resolution
def resolve_command(self, ctx, args):
"Return the full command name if changed"
_, cmd, args = super().resolve_command(ctx, args)
if _ != cmd.name:
logger.debug(f"Rewrite command '{_}' to '{cmd.name}'")
return cmd.name, cmd, args
def get_command(self, ctx, cmd_name: str): def get_command(self, ctx, cmd_name: str):
"""Given a context and a command name, this returns a :class:`Command` """Given a context and a command name, this returns a :class:`Command`
object if it exists or returns ``None``. object if it exists or returns ``None``.
@ -18,19 +67,37 @@ class NestedHelpGroup(click.Group):
# Resolve name part by part # Resolve name part by part
parts = cmd_name.split(" ") parts = cmd_name.split(" ")
len_parts = len(parts) -1
curr = self curr = self
for idx, part in enumerate(parts): for idx, part in enumerate(parts):
curr = curr.commands.get(part) match = curr.commands.get(part)
# Look for shortcut if last part
if match is None:
# Look for direct children only
matches = [x for x in self._resolve_children(self, ctx=ctx, ignore_groups=False, deep=0)
if x.startswith(cmd_name)]
# Look for possible matches
if not matches:
pass
elif len(matches) == 1:
match = click.Group.get_command(self, ctx, matches[0])
else:
ctx.fail(f"Too many matches for {cmd_name}: {', '.join(sorted(matches))}")
# Iterate over next child!
curr = match
return curr return curr
def list_commands(self, ctx) -> list[str]: def list_commands(self, ctx) -> list[str]:
"List all children commands" "List all children commands"
return self._resolve_children(self, ctx=ctx, ignore_groups=True) return sorted(self._resolve_children(self, ctx=ctx, ignore_groups=True))
@classmethod @classmethod
def _resolve_children(cls, obj, ctx=None, ignore_groups=False, _parent=None): def _resolve_children(cls, obj, ctx=None, ignore_groups=False, _parent=None, deep=-1):
"Resolve recursively all children" "Resolve recursively all children"
# Source: Adapted from https://stackoverflow.com/a/56159096 # Source: Adapted from https://stackoverflow.com/a/56159096
@ -51,12 +118,15 @@ class NestedHelpGroup(click.Group):
ret.append(record) ret.append(record)
# Recursive loop # Recursive loop
ret.extend( if deep != 0:
cls._resolve_children( deep = deep -1
child, ctx=ctx, ignore_groups=ignore_groups, _parent=full_name ret.extend(
cls._resolve_children(
child, ctx=ctx, ignore_groups=ignore_groups, _parent=full_name, deep=deep
)
) )
)
return ret return ret
return [] return []

View File

@ -83,6 +83,20 @@ def prune(items):
raise Exception(f"Function prune requires a list or a dict, got: {items}") raise Exception(f"Function prune requires a list or a dict, got: {items}")
return ret return ret
# from jinja2 import Environment, FileSystemLoader, meta
# env = Environment(loader=FileSystemLoader('templates'))
# parsed_content = env.parse(payload)
from jinja2 import Environment, PackageLoader, meta
def jinja_template_vars(payload):
env = Environment() #loader=PackageLoader('templates'))
parsed_content = env.parse(payload)
return meta.find_undeclared_variables(parsed_content)
def jinja_render(payload, vars): def jinja_render(payload, vars):
"Parse a string with jinja" "Parse a string with jinja"

View File

@ -43,19 +43,19 @@ plugin_base = {
"commands": { "commands": {
"shell_enable": { "shell_enable": {
"desc": "Enable shell ident", "desc": "Enable shell ident",
"cmd": "export SHELL_IDENT={{ident}}", "shell": "export SHELL_IDENT={{ident}}",
}, },
"shell_disable": { "shell_disable": {
"desc": "Disable shell ident", "desc": "Disable shell ident",
"cmd": "unset SHELL_IDENT", "shell": "unset SHELL_IDENT",
}, },
"id new": { "id new": {
"desc": "Create shell identy", "desc": "Create shell identy",
"cmd": "add_ident {{ param }}", "shell": "add_ident {{ param }}",
}, },
"id delete": { "id delete": {
"desc": "Delete shell identy", "desc": "Delete shell identy",
"cmd": "rm_ident {{ param }}", "shell": "rm_ident {{ param }}",
}, },
}, },
}, },

View File

@ -17,13 +17,13 @@ providers:
shell_enable: shell_enable:
desc: Enable git identity desc: Enable git identity
cmd: | shell: |
export GH_TOKEN='{{gh_token}}' export GH_TOKEN='{{gh_token}}'
export GH_REPO='{{ident}}' export GH_REPO='{{ident}}'
shell_disable: shell_disable:
desc: Disable git identity desc: Disable git identity
cmd: | shell: |
unset GH_TOKEN GH_REPO unset GH_TOKEN GH_REPO
@ -69,13 +69,13 @@ providers:
shell_enable: shell_enable:
desc: Enable git identity desc: Enable git identity
cmd: | shell: |
export GITEA_SERVER_URL='{{gitea_server_url}}' export GITEA_SERVER_URL='{{gitea_server_url}}'
export GITEA_LOGIN='{{email}}' export GITEA_LOGIN='{{email}}'
shell_disable: shell_disable:
desc: Disable git identity desc: Disable git identity
cmd: | shell: |
unset GITEA_SERVER_URL GITEA_LOGIN unset GITEA_SERVER_URL GITEA_LOGIN
@ -120,7 +120,7 @@ providers:
shell_enable: shell_enable:
desc: Enable minio alias desc: Enable minio alias
cmd: | shell: |
export MINIO_ACCESS_KEY={{minio_access}} export MINIO_ACCESS_KEY={{minio_access}}
export MINIO_SECRET_KEY={{minio_secret}} export MINIO_SECRET_KEY={{minio_secret}}
# cmd_FUTURE: | # cmd_FUTURE: |
@ -130,18 +130,18 @@ providers:
shell_disable: shell_disable:
desc: Disable minio alias desc: Disable minio alias
cmd: | shell: |
unset MINIO_ACCESS_KEY MINIO_SECRET_KEY unset MINIO_ACCESS_KEY MINIO_SECRET_KEY
minio new alias: minio new alias:
desc: Create alias desc: Create alias
cmd: | shell: |
mc alias set {{ident}} {{api_url}} {{minio_user}} {{minio_password}} mc alias set {{ident}} {{api_url}} {{minio_user}} {{minio_password}}
minio delete alias: minio delete alias:
desc: Remove alias desc: Remove alias
cmd: | shell: |
mc alias rm {{ident}} mc alias rm {{ident}}

View File

@ -5,174 +5,8 @@ from iam.lib.utils import get_pkg_dir, open_yaml, to_yaml
yml_dir = get_pkg_dir(__name__) yml_dir = get_pkg_dir(__name__)
plugin_conf = open_yaml(os.path.join(yml_dir, "local.yml"))[0] plugin_conf = open_yaml(os.path.join(yml_dir, "local.yml"))[0]
all = plugin_conf.get("providers", {}) all = plugin_conf.get("providers", {})
# plugin_ssh = {
# "services": {
# "local.ssh_key": {
# "desc": "Local ssh key",
# # "input": {
# # "ssh_agent_socket_dir": "/run/user/ssh-agent",
# # "ssh_agent_tmout": "7d",
# # },
# "commands": {
# "shell_enable": {
# "cmd": """
# export SSH_AUTH_SOCK={{ssh_agent_socket_dir}}/{{user}} && \
# ssh-agent -a $SSH_AUTH_SOCK -t {{ssh_agent_tmout}}
# """,
# },
# "shell_disable": {
# "cmd": "ssh-agent -k && unset SSH_AUTH_SOCK",
# },
# },
# },
# },
# "resources_def": {
# "service.local.ssh_key": {
# "desc": "A local ssh key",
# },
# "auth.ssh_certificate": {
# "desc": "SSH Certificates",
# "input": {"ssh_cert_file": None},
# "needs": ["auth.ssh_key"],
# },
# "auth.ssh_key": {
# "desc": "ssh_key",
# "input": {
# "ssh_key_file": None,
# "ssh_key_secret": None
# },
# "needs": [
# {"kind": "auth.password", "remap": {"ssh_key_secret": "passord"}}
# ],
# },
# "account.ssh": {"desc": "An unix account", "input": {"host": None}},
# },
# "resources": {
# "service.local.ssh_agent": {
# "enabled": True,
# # "contains": [
# # "auth.ssh_key:{ident}/ed25519",
# # "auth.ssh_key:{ident}/rsa4096",
# # "auth.ssh_key:{ident}/rsa2048",
# # "auth.ssh_key:{ident}/rsa1024",
# # "auth.ssh_key:{ident}",
# # ],
# },
# "service.local.ssh_agent_keys": {
# "enabled": True,
# "loop_limit": 3,
# "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}",
# ],
# },
# },
# }
# plugin_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_enable": {
# "cmd": """
# export SSH_AUTH_SOCK={{ssh_agent_socket_dir}}/{{user}} && \
# ssh-agent -a $SSH_AUTH_SOCK -t {{ssh_agent_tmout}}
# """,
# },
# "shell_disable": {
# "cmd": "ssh-agent -k && unset SSH_AUTH_SOCK",
# },
# },
# },
# "local.ssh_agent_keys": {
# "desc": "Local ssh-agent keys",
# "required_services": [
# "local.ssh_agent"
# ],
# "commands": {
# "shell_enable": {
# "cmd": """
# ssh-add {% for item in loop %} {{item.ssh_key_file}} {% endfor %}
# """,
# },
# "shell_disable": {
# "cmd": "ssh-agent -d {ssh_key_file}",
# },
# },
# },
# },
# "resources_def": {
# "service.local.ssh_agent": {
# "desc": "A local ssh_agent service",
# },
# "service.local.ssh_agent_keys": {
# "desc": "A local ssh_agent_keys service",
# },
# },
# "resources": {
# "service.local.ssh_agent": {
# "enabled": True,
# # "contains": [
# # "auth.ssh_key:{ident}/ed25519",
# # "auth.ssh_key:{ident}/rsa4096",
# # "auth.ssh_key:{ident}/rsa2048",
# # "auth.ssh_key:{ident}/rsa1024",
# # "auth.ssh_key:{ident}",
# # ],
# },
# "service.local.ssh_agent_keys": {
# "enabled": True,
# "loop_limit": 3,
# "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}",
# ],
# },
# },
# }
# all = {
# "ssh": plugin_ssh,
# "ssh_agent": plugin_ssh_agent,
# }
# print (to_yaml(all))

View File

@ -17,18 +17,32 @@ providers:
ssh new: ssh new:
desc: Create new SSH key 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: | shell: |
# env | sort
# echo
SSH_KEY_ALG={{ssh_key_alg}} SSH_KEY_ALG={{ssh_key_alg}}
SSH_KEY_VERSION="$(date +'%Y%m%d')" SSH_KEY_VERSION="$(date +'%Y%m%d')"
SSH_KEY_HOST="$(hostname -f)" SSH_KEY_HOST="$(hostname -f)"
SSH_KEY_FILE=$HOME/.ssh/{ident}/{user}_${SSH_KEY_ALG}_${SSH_KEY_VERSION} 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} SSH_KEY_COMMENT={{user}}@${SSH_KEY_HOST}:${SSH_KEY_ALG}_${SSH_KEY_VERSION}
ssh-keygen -f "{SSH_KEY_FILE}" \ echo mkdir -p $HOME/.ssh/{{ident}}/
echo ssh-keygen -f "${SSH_KEY_FILE}" \
-t ed25519 -a 100 \ -t ed25519 -a 100 \
-N "{{ssh_key_secret}}" \ {% if ssh_key_secret %}-N "{{ssh_key_secret}}"{%endif%} \
-C "$SSH_KEY_COMMENT" -C "$SSH_KEY_COMMENT"
@ -36,7 +50,7 @@ providers:
ssh delete: ssh delete:
desc: Delete existing SSH key desc: Delete existing SSH key
cmd: | cmd: |
find $HOME/.ssh/{ident}/ -name "{user}_*" find $HOME/.ssh/{{ident}}/ -name "{{user}}_*"
@ -124,7 +138,7 @@ providers:
shell_start: shell_start:
desc: Start ssh-agent desc: Start ssh-agent
cmd: | shell: |
socket=$HOME/.local/state/ssh-agent/{{user}} socket=$HOME/.local/state/ssh-agent/{{user}}
start=true start=true
@ -154,7 +168,7 @@ providers:
shell_enable: shell_enable:
desc: Enable ssh-agent desc: Enable ssh-agent
cmd: | shell: |
socket=$HOME/.local/state/ssh-agent/{{user}} socket=$HOME/.local/state/ssh-agent/{{user}}
if [[ -e "$socket.env" ]]; then if [[ -e "$socket.env" ]]; then
@ -168,13 +182,13 @@ providers:
shell_disable: shell_disable:
desc: Disable ssh-agent desc: Disable ssh-agent
cmd: | shell: |
unset SSH_AUTH_SOCK SSH_AGENT_PID unset SSH_AUTH_SOCK SSH_AGENT_PID
shell_stop: shell_stop:
desc: Kill ssh-agent desc: Kill ssh-agent
cmd: | shell: |
socket=$HOME/.local/state/ssh-agent/{{user}} socket=$HOME/.local/state/ssh-agent/{{user}}
if [[ -e "$socket.env" ]]; then if [[ -e "$socket.env" ]]; then
@ -205,11 +219,11 @@ providers:
commands: commands:
ssh add: ssh add:
desc: Unload keys into ssh-agent desc: Unload keys into ssh-agent
cmd: ssh-agent -d {ssh_key_file} shell: ssh-agent -d {ssh_key_file}
ssh rm: ssh rm:
desc: Load keys into ssh-agent desc: Load keys into ssh-agent
cmd: | shell: |
ssh-add {% for item in loop %} {{item.ssh_key_file}} {% endfor %} ssh-add {% for item in loop %} {{item.ssh_key_file}} {% endfor %}
@ -261,7 +275,7 @@ providers:
shell_enable: shell_enable:
desc: Enable git identity desc: Enable git identity
cmd: | shell: |
export GIT_AUTHOR_NAME='{{ident}}' export GIT_AUTHOR_NAME='{{ident}}'
export GIT_AUTHOR_EMAIL='{{email}}' export GIT_AUTHOR_EMAIL='{{email}}'
export GIT_COMMITTER_NAME='{{ident}}' export GIT_COMMITTER_NAME='{{ident}}'
@ -270,7 +284,7 @@ providers:
shell_disable: shell_disable:
desc: Disable git identity desc: Disable git identity
cmd: | shell: |
unset GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL unset GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL
@ -284,13 +298,13 @@ providers:
shell_enable: shell_enable:
desc: Enable git home management desc: Enable git home management
cmd: | shell: |
export GIT_DIR="{{git_dir}}" export GIT_DIR="{{git_dir}}"
export GIT_WORK_TREE="{{git_work_tree}}/{{ ident }}" export GIT_WORK_TREE="{{git_work_tree}}/{{ ident }}"
shell_disable: shell_disable:
desc: Disable git home management desc: Disable git home management
cmd: | shell: |
unset GIT_DIR GIT_WORK_TREE unset GIT_DIR GIT_WORK_TREE
required_services: required_services:
@ -331,13 +345,13 @@ providers:
shell_enable: shell_enable:
desc: Enable PS1 desc: Enable PS1
cmd: | shell: |
export OLD_PS1=$PS1 export OLD_PS1=$PS1
export PS1="\033[0;34m\]({{ident}})\033[00m\] ${PS1}" export PS1="\033[0;34m\]({{ident}})\033[00m\] ${PS1}"
shell_disable: shell_disable:
desc: Disable PS1 desc: Disable PS1
cmd: | shell: |
export PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ ' export PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
# export PS1="$OLD_PS1" # export PS1="$OLD_PS1"

13
poetry.lock generated
View File

@ -424,6 +424,17 @@ rich = ">=10.7.0"
[package.extras] [package.extras]
dev = ["pre-commit"] dev = ["pre-commit"]
[[package]]
name = "sh"
version = "2.0.6"
description = "Python subprocess replacement"
optional = false
python-versions = ">=3.8.1,<4.0"
files = [
{file = "sh-2.0.6-py3-none-any.whl", hash = "sha256:ced8f2e081a858b66a46ace3703dec243779abbd5a1887ba7e3c34f34da70cd2"},
{file = "sh-2.0.6.tar.gz", hash = "sha256:9b2998f313f201c777e2c0061f0b1367497097ef13388595be147e2a00bf7ba1"},
]
[[package]] [[package]]
name = "typer" name = "typer"
version = "0.9.0" version = "0.9.0"
@ -459,4 +470,4 @@ files = [
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.11" python-versions = "^3.11"
content-hash = "1747400a30523d50e0ccaf55386f60f05b4d6e5e222aeb8a737d4e78b702a210" content-hash = "2673f8f06cf73701e5dac7ef463abeaf01bfb40b5c003e0898c7d97d0c589158"

View File

@ -21,6 +21,7 @@ coloredlogs = "^15.0.1"
rich = "^13.6.0" rich = "^13.6.0"
rich-click = "^1.6.1" rich-click = "^1.6.1"
colorama = "^0.4.6" colorama = "^0.4.6"
sh = "^2.0.6"
[tool.poetry.scripts] [tool.poetry.scripts]