967 lines
24 KiB
Python
Executable File
967 lines
24 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import io
|
|
import logging
|
|
import os
|
|
import sys
|
|
from enum import Enum
|
|
from pprint import pprint
|
|
from types import SimpleNamespace
|
|
|
|
from iam.app import App
|
|
from iam.framework import get_app_logger
|
|
|
|
from . import exceptions as error
|
|
from .lib.cli_views import VIEW_FORMATS_NAMES, ViewItem, ViewList
|
|
from .lib.click_utils import NestedHelpGroup
|
|
# from .cli_views import ViewItem, ViewList, VIEW_FORMATS
|
|
from .lib.utils import prune, to_yaml
|
|
|
|
# import rich
|
|
# from rich import box
|
|
# from rich.console import Console
|
|
# from rich.pretty import pprint
|
|
# from rich.prompt import Prompt
|
|
# from rich.table import Table
|
|
|
|
|
|
# from rich.pretty import pprint
|
|
|
|
|
|
# Rich loader
|
|
# ======================
|
|
|
|
APP_RICH = True
|
|
table_box_params = {}
|
|
|
|
if not APP_RICH:
|
|
import click
|
|
|
|
# Create console
|
|
console = SimpleNamespace(print=print)
|
|
else:
|
|
# Import rich
|
|
# Transparent rich proxy for click (help menus)
|
|
import rich_click as click
|
|
from rich_click import RichGroup
|
|
|
|
from rich import box #, print
|
|
from rich.console import Console
|
|
# Overrides defaults
|
|
from rich.pretty import pprint
|
|
|
|
# Load rich wrappers
|
|
class NestedRichHelpGroup(NestedHelpGroup, RichGroup):
|
|
"Add rich class"
|
|
|
|
NestedHelpGroup = NestedRichHelpGroup
|
|
|
|
# Create default rich table settings
|
|
table_box_params = {
|
|
"box": box.ASCII2, # box.MINIMAL
|
|
"min_width": 60,
|
|
}
|
|
|
|
# Create a console
|
|
console = Console()
|
|
|
|
|
|
logger = logging.getLogger()
|
|
cli_logger = logging.getLogger("iam.cli")
|
|
|
|
|
|
# Global vars
|
|
# ======================
|
|
|
|
|
|
# Init app
|
|
# ======================
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
item_view = ViewItem(output=console.print)
|
|
list_view = ViewList(output=console.print)
|
|
|
|
|
|
# General App settings
|
|
# ===============================
|
|
|
|
|
|
APP_NAME = "iam"
|
|
APP_VERSION = "__version__TOFIX"
|
|
APP_EXCEPTION = error.IamException
|
|
|
|
|
|
# See: https://docs.python.org/3.8/library/logging.html#logging-levels
|
|
DEFAULT_LOG_LEVEL = 30 # warning
|
|
|
|
# Get default ident
|
|
DEBUG = os.environ.get("IAM_DEBUG", "False")
|
|
DEFAULT_IDENT = os.environ.get("IAM_IDENT", os.environ.get("SHELL_IDENT", ""))
|
|
DEFAULT_FORMAT = os.environ.get("IAM_FORMAT", "table")
|
|
DEFAULT_HELP_OPTIONS=["-h", "--help"]
|
|
DEFAULT_SHELL = os.environ.get("SHELL", "bash")
|
|
|
|
# list_view.formats_enum
|
|
|
|
# Click helpers
|
|
# ===============================
|
|
|
|
_cmd1_options = [click.option("--format")]
|
|
|
|
_cmd2_options = [click.option("--cmd2-opt")]
|
|
|
|
CONTEXT_SETTINGS = dict(
|
|
show_default=True,
|
|
help_option_names=DEFAULT_HELP_OPTIONS,
|
|
auto_envvar_prefix=APP_NAME.upper(),
|
|
)
|
|
|
|
|
|
def global_options(function):
|
|
# function = click.option(
|
|
# "debug", "--debug",
|
|
# is_flag=True, show_default=False, default=False,
|
|
# help="Debug"
|
|
# )(function)
|
|
# function = click.option(
|
|
# "force", "--force", "-f"
|
|
# 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(
|
|
# "recursive", "--recursive", "-r"
|
|
# is_flag=True, show_default=False, default=False,
|
|
# help="Recursive mode"
|
|
# )(function)
|
|
|
|
function = click.option(
|
|
"quiet",
|
|
"--quiet",
|
|
"-q",
|
|
is_flag=True,
|
|
show_default=False,
|
|
default=False,
|
|
help="Suppress output except warnings and errors.",
|
|
)(function)
|
|
|
|
function = click.option(
|
|
"log_trace",
|
|
"--trace",
|
|
is_flag=True,
|
|
show_default=False,
|
|
default=False,
|
|
help="Show traceback on errors",
|
|
)(function)
|
|
|
|
function = click.option(
|
|
"-v",
|
|
"--verbose",
|
|
count=True,
|
|
type=click.IntRange(0, int(DEFAULT_LOG_LEVEL / 10), clamp=True),
|
|
help="Increase verbosity of output. Can be repeated.",
|
|
)(function)
|
|
|
|
return 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",
|
|
"--verbose",
|
|
count=True,
|
|
type=click.IntRange(0, int(DEFAULT_LOG_LEVEL / 10), clamp=True),
|
|
help="Increase verbosity of output. Can be repeated.",
|
|
)(function)
|
|
|
|
# Special output options
|
|
function = click.option(
|
|
"fmt_name",
|
|
"-O",
|
|
"--output",
|
|
type=click.Choice(VIEW_FORMATS_NAMES, case_sensitive=False),
|
|
help="Output format",
|
|
default=None,
|
|
show_default=DEFAULT_FORMAT,
|
|
)(function)
|
|
|
|
function = click.option(
|
|
"fmt_fields", "-H", "--headers", help="Show specific headers"
|
|
)(function)
|
|
|
|
function = click.option("fmt_sort", "-S", "--sort", help="Sort columns")(function)
|
|
|
|
return function
|
|
|
|
|
|
# Base Application example
|
|
# ===============================
|
|
|
|
# DEFAULT_CONFIG = os.environ.get("IAM_CONFIG", "MISSING")
|
|
|
|
DEFAULT_CONFIG = click.get_app_dir(APP_NAME)
|
|
# SHOW_DEFAULT_HELP=True
|
|
|
|
|
|
def get_var(name, default=None):
|
|
real_name = f"{APP_NAME.upper()}_{name.upper()}"
|
|
# print ("QUERY", real_name)
|
|
return os.environ.get(real_name, default)
|
|
|
|
|
|
# @click.group(cls=NestedHelpGroup, context_settings=CONTEXT_SETTINGS)
|
|
@click.group(cls=NestedHelpGroup
|
|
)
|
|
@global_options
|
|
@format_options
|
|
@click.version_option()
|
|
@click.option(
|
|
"config",
|
|
"--config",
|
|
"-C",
|
|
# envvar='CONFIG', # multiple=True,
|
|
type=click.Path(),
|
|
default=get_var("CONFIG", DEFAULT_CONFIG),
|
|
show_default=get_var("CONFIG", DEFAULT_CONFIG),
|
|
help="Configuration directory or file",
|
|
)
|
|
@click.pass_context
|
|
def cli_app(
|
|
ctx,
|
|
verbose,
|
|
config: str = DEFAULT_IDENT,
|
|
ident: str = DEFAULT_IDENT,
|
|
quiet=False,
|
|
log_trace=False,
|
|
fmt_fields=None,
|
|
fmt_sort=None,
|
|
fmt_name=None,
|
|
):
|
|
"""Naval Fate.
|
|
|
|
This is the docopt example adopted to Click but with some actual
|
|
commands implemented and not just the empty parsing which really
|
|
is not all that interesting.
|
|
"""
|
|
|
|
# Calculate log level
|
|
log_level = DEFAULT_LOG_LEVEL - verbose * 10
|
|
log_level = log_level if log_level > 0 else 10
|
|
|
|
# Define loggers
|
|
loggers = {
|
|
"iam": {"level": log_level},
|
|
"iam.cli": {"level": "DEBUG", "handlers": ["info"]},
|
|
}
|
|
|
|
# Instanciate logger
|
|
get_app_logger(loggers=loggers, level=log_level, colors=True)
|
|
|
|
cli_config = SimpleNamespace(
|
|
log_level=log_level,
|
|
fmt_name=fmt_name or DEFAULT_FORMAT,
|
|
ident=ident,
|
|
)
|
|
render_config = {
|
|
"fmt_name": cli_config.fmt_name,
|
|
"table_settings": table_box_params,
|
|
}
|
|
|
|
render_item = lambda *args, conf={}, **kwargs: item_view.render(
|
|
*args, conf={**render_config, **prune(conf)}, **kwargs
|
|
)
|
|
render_list = lambda *args, conf={}, **kwargs: list_view.render(
|
|
*args, conf={**render_config, **prune(conf)}, **kwargs
|
|
)
|
|
|
|
# Instanciate app
|
|
logger.info(f"Current ident: {ident}")
|
|
ctx.obj = SimpleNamespace(
|
|
app=App(config_path=config, ident=ident),
|
|
cli=cli_config,
|
|
render_item=render_item,
|
|
render_list=render_list,
|
|
)
|
|
|
|
|
|
@cli_app.command()
|
|
@click.pass_context
|
|
def tests(ctx):
|
|
print("Hello world")
|
|
|
|
return
|
|
|
|
# pprint(command_tree(cli_app))
|
|
|
|
print("TREEE @")
|
|
pprint(command_tree3(cli_app, ctx))
|
|
|
|
all_help(cli_app, ctx)
|
|
|
|
# subcommand_obj = cli_app.get_command(ctx, subcommand)
|
|
# if subcommand_obj is None:
|
|
# click.echo("I don't know that command.")
|
|
# else:
|
|
# pprint (subcommand_obj)
|
|
# pprint (subcommand_obj.__dict__)
|
|
# click.echo(subcommand_obj.get_help(ctx))
|
|
|
|
|
|
# Cli Utils
|
|
# ===============================
|
|
|
|
|
|
# Dedicated help command
|
|
# Source: https://stackoverflow.com/a/53137265
|
|
@cli_app.command()
|
|
@click.argument("subcommand", nargs=-1)
|
|
@click.pass_context
|
|
def help(ctx, subcommand):
|
|
"Show a command help"
|
|
|
|
subcommand = " ".join(subcommand)
|
|
subcommand_obj = cli_app.get_command(ctx, subcommand)
|
|
if subcommand_obj is None:
|
|
click.echo("I don't know that command.")
|
|
else:
|
|
pprint(subcommand_obj)
|
|
pprint(subcommand_obj.__dict__)
|
|
click.echo(subcommand_obj.get_help(ctx))
|
|
|
|
|
|
# Global commands
|
|
# ===============================
|
|
|
|
|
|
@cli_app.command()
|
|
@click.pass_context
|
|
def dump(ctx):
|
|
ctx.obj.app.user_dump()
|
|
|
|
|
|
# Resource kind management
|
|
# ===============================
|
|
|
|
|
|
@cli_app.group("kind")
|
|
def cli__kind():
|
|
"""Manages Resources Kind."""
|
|
|
|
|
|
@cli__kind.command("list")
|
|
@click.argument("filter", required=False)
|
|
@format_options
|
|
@click.pass_context
|
|
def kind_list(ctx, filter, fmt_name=None, fmt_sort=None, fmt_fields=None, verbose=None):
|
|
"List resource kinds"
|
|
|
|
# Fetch data
|
|
# filter = name
|
|
resources_kind = ctx.obj.app.catalog.resources_kind
|
|
data = []
|
|
for name, item in sorted(resources_kind.items(), key=lambda key: key):
|
|
if filter and not name.startswith(filter):
|
|
continue
|
|
data.append((name, item.desc))
|
|
|
|
# Render data
|
|
columns = ["name", "desc"]
|
|
conf = {
|
|
"table_title": "Resources kinds listing",
|
|
"fmt_name": fmt_name,
|
|
"fmt_sort": fmt_sort,
|
|
"fmt_fields": fmt_fields,
|
|
}
|
|
ctx.obj.render_list(data, columns, conf=conf)
|
|
|
|
|
|
@cli__kind.command("show")
|
|
@click.argument("name", required=False)
|
|
@format_options
|
|
@click.pass_context
|
|
def kind_show(ctx, name, fmt_name=None, fmt_fields=None, fmt_sort=None, verbose=None):
|
|
"Show resource kind"
|
|
|
|
# Fetch data
|
|
columns = ["name", "desc", "input", "needs", "remap"]
|
|
filter = name
|
|
resources_kind = ctx.obj.app.catalog.resources_kind
|
|
for name, item in sorted(resources_kind.items(), key=lambda key: key):
|
|
if filter and not name.startswith(filter):
|
|
continue
|
|
|
|
data = [
|
|
item.name,
|
|
item.desc,
|
|
f"{to_yaml(item.input)}",
|
|
f"{to_yaml(item.needs)}",
|
|
f"{to_yaml(item.remap)}",
|
|
]
|
|
|
|
# Render data
|
|
conf = {
|
|
"table_title": f"Resources kind: {data[0]}",
|
|
"fmt_name": fmt_name,
|
|
"fmt_sort": fmt_sort,
|
|
"fmt_fields": fmt_fields,
|
|
}
|
|
ctx.obj.render_item(data, columns, conf=conf)
|
|
|
|
|
|
# @cli__kind.command("move")
|
|
# @click.argument("cli__kind")
|
|
# @click.argument("x", type=float)
|
|
# @click.argument("y", type=float)
|
|
# @click.option("--speed", metavar="KN", default=10, help="Speed in knots.")
|
|
# def ship_move(cli__kind, x, y, speed):
|
|
# """Moves SHIP to the new location X,Y."""
|
|
# click.echo(f"Moving cli__kind {cli__kind} to {x},{y} with speed {speed}")
|
|
|
|
|
|
# @cli__kind.command("shoot")
|
|
# @click.argument("cli__kind")
|
|
# @click.argument("x", type=float)
|
|
# @click.argument("y", type=float)
|
|
# def ship_shoot(cli__kind, x, y):
|
|
# """Makes SHIP fire to X,Y."""
|
|
# click.echo(f"Ship {cli__kind} fires to {x},{y}")
|
|
|
|
|
|
# Resource res management
|
|
# ===============================
|
|
|
|
|
|
@cli_app.group("res")
|
|
def cli__res():
|
|
"""Manages Resources."""
|
|
|
|
|
|
@cli__res.command("list")
|
|
@click.argument("filter", required=False)
|
|
# @format_options
|
|
@click.pass_context
|
|
def res_list(ctx, filter, fmt_name=None, fmt_fields=None, fmt_sort=None, verbose=None):
|
|
"List resource ress"
|
|
|
|
# Fetch data
|
|
resources = ctx.obj.app.catalog.resources
|
|
data = []
|
|
for name, res in sorted(resources.items(), key=lambda key: key):
|
|
if filter and not name.startswith(filter):
|
|
continue
|
|
data.append(
|
|
(
|
|
res.get_kind(),
|
|
res.get_name(),
|
|
res.desc,
|
|
)
|
|
)
|
|
|
|
# Render data
|
|
columns = ["kind", "name", "desc"]
|
|
conf = {
|
|
"table_title": "Resources listing",
|
|
"fmt_name": fmt_name,
|
|
"fmt_sort": fmt_sort,
|
|
"fmt_fields": fmt_fields,
|
|
}
|
|
ctx.obj.render_list(data, columns, conf=conf)
|
|
|
|
|
|
@cli__res.command("show")
|
|
@click.argument("name", required=False)
|
|
@format_options
|
|
@click.pass_context
|
|
def res_show(ctx, name, fmt_name=None, fmt_fields=None, fmt_sort=None, verbose=None):
|
|
"Show resource res"
|
|
|
|
# Fetch data
|
|
columns = ["name", "uses", "input"]
|
|
columns_all = columns + ["active", "deps", "missing", "loop_max", "loop", "need"]
|
|
filter = name
|
|
resources = ctx.obj.app.catalog.resources.select("startswith", name)
|
|
for name, item in sorted(resources.items(), key=lambda key: key):
|
|
if filter and not name.startswith(filter):
|
|
continue
|
|
|
|
if not all and not item.is_active():
|
|
continue
|
|
|
|
data = [
|
|
name,
|
|
to_yaml(item.uses).strip(),
|
|
to_yaml(item.input).strip(),
|
|
]
|
|
if all:
|
|
data.extend(
|
|
[
|
|
item.is_active(),
|
|
",".join([x.name for x in item.resources_deps]),
|
|
",".join(item.resources_missing),
|
|
item.loop_limit,
|
|
to_yaml(item.loop).strip(),
|
|
to_yaml(item.needs).strip(),
|
|
]
|
|
)
|
|
|
|
# Render data
|
|
ctx.obj.render_item(
|
|
data,
|
|
columns_all if all else columns,
|
|
conf={
|
|
"table_title": f"Resources kind: {data[0]}",
|
|
"fmt_name": fmt_name,
|
|
"fmt_sort": fmt_sort,
|
|
"fmt_fields": fmt_fields,
|
|
},
|
|
)
|
|
|
|
|
|
# Resource svc management
|
|
# ===============================
|
|
|
|
|
|
@cli_app.group("svc")
|
|
def cli__svc():
|
|
"""Manages Resources Kind."""
|
|
|
|
|
|
@cli__svc.command("list")
|
|
@click.argument("filter", required=False)
|
|
@format_options
|
|
@click.pass_context
|
|
def svc_list(ctx, filter, fmt_name=None, fmt_fields=None, fmt_sort=None, verbose=None):
|
|
"List services"
|
|
|
|
columns = ["name", "desc"]
|
|
services = ctx.obj.app.catalog.services
|
|
data = []
|
|
for name, res in sorted(services.items(), key=lambda key: key[1].name):
|
|
if filter and not name.startswith(filter):
|
|
continue
|
|
|
|
# if not all and not res.is_active():
|
|
# continue
|
|
|
|
data.append(
|
|
(
|
|
# res.get_kind(),
|
|
# res.get_name(),
|
|
res.name,
|
|
res.desc,
|
|
)
|
|
)
|
|
|
|
# Render data
|
|
ctx.obj.render_list(
|
|
data,
|
|
columns,
|
|
conf={
|
|
"table_title": f"Services listing",
|
|
"fmt_name": fmt_name,
|
|
"fmt_sort": fmt_sort,
|
|
"fmt_fields": fmt_fields,
|
|
},
|
|
)
|
|
|
|
|
|
@cli__svc.command("show")
|
|
@click.argument("name", required=False)
|
|
@format_options
|
|
@click.pass_context
|
|
def svc_show(ctx, name, fmt_name=None, fmt_fields=None, fmt_sort=None, verbose=None):
|
|
"Show service"
|
|
|
|
columns = [
|
|
"name",
|
|
"desc",
|
|
"enabled",
|
|
"input",
|
|
"commands",
|
|
"required_svc",
|
|
"resource_lookup",
|
|
"rest_matches" "dump",
|
|
]
|
|
columns_all = columns + ["active", "deps", "missing", "loop_max", "loop", "need"]
|
|
|
|
output = {}
|
|
services = ctx.obj.app.catalog.services.select("startswith", name)
|
|
for name, svc in services.items():
|
|
assert name == svc.name
|
|
|
|
data = [
|
|
svc.name,
|
|
svc.desc,
|
|
svc.enabled,
|
|
svc.input,
|
|
to_yaml(svc.list_cmds()).strip(),
|
|
svc.required_services,
|
|
svc.resources_lookup,
|
|
svc.resources_matches,
|
|
svc.dump_item(),
|
|
]
|
|
data = [str(item) for item in data]
|
|
|
|
ctx.obj.render_item(
|
|
data,
|
|
columns_all if all else columns,
|
|
conf={
|
|
"table_title": f"Service show: {data[0]}",
|
|
"fmt_name": fmt_name,
|
|
"fmt_sort": fmt_sort,
|
|
"fmt_fields": fmt_fields,
|
|
},
|
|
)
|
|
|
|
|
|
@cli__svc.command("commands")
|
|
@format_options
|
|
@click.pass_context
|
|
def svc_commands(ctx, fmt_name=None, fmt_fields=None, fmt_sort=None, verbose=None):
|
|
"Show service"
|
|
|
|
columns = ["service", "name", "type", "desc"]
|
|
|
|
data = []
|
|
for svc_name, svc in ctx.obj.app.list_services().items():
|
|
cmds = svc.list_cmds()
|
|
for source in ["shell", "cmds"]:
|
|
items = cmds[source]
|
|
# target = ret[source]
|
|
for cmd_name, conf in items.items():
|
|
# target[cmd_name] = conf
|
|
|
|
data.append([svc_name, cmd_name, source, conf])
|
|
|
|
# Render data
|
|
ctx.obj.render_list(
|
|
data,
|
|
columns,
|
|
conf={
|
|
"table_title": f"Services commands",
|
|
"fmt_name": fmt_name,
|
|
"fmt_sort": fmt_sort,
|
|
"fmt_fields": fmt_fields,
|
|
},
|
|
)
|
|
|
|
|
|
@cli__svc.command("run_v1")
|
|
@click.argument("name")
|
|
@click.argument("command")
|
|
@format_options
|
|
@click.pass_context
|
|
def svc_run(
|
|
ctx, name, command, fmt_name=None, fmt_fields=None, fmt_sort=None, verbose=None
|
|
):
|
|
"Show service"
|
|
|
|
ret = ctx.obj.app.catalog.services.get(name)
|
|
tmp = ret.run_cmd(command)
|
|
pprint(tmp)
|
|
|
|
|
|
# Shell management
|
|
# ======================
|
|
|
|
|
|
@cli_app.group("shell")
|
|
def cli__shell():
|
|
"""Manages Resources Kind."""
|
|
|
|
|
|
@cli__shell.command("idents")
|
|
@format_options
|
|
@click.pass_context
|
|
def shell_idents(
|
|
ctx, fmt_name=None, fmt_fields=None, fmt_sort=None, verbose=None,
|
|
):
|
|
"Shell identities"
|
|
columns = ["name"]
|
|
data = [[x] for x in ctx.obj.app.get_idents()]
|
|
|
|
# Render data
|
|
ctx.obj.render_list(
|
|
data,
|
|
columns,
|
|
conf={
|
|
"table_title": f"Shell identities",
|
|
"fmt_name": fmt_name,
|
|
"fmt_sort": fmt_sort,
|
|
"fmt_fields": fmt_fields,
|
|
},
|
|
)
|
|
|
|
|
|
@cli__shell.command("order")
|
|
@click.option(
|
|
"--reverse", is_flag=True, show_default=False, default=False, help="Reverse order"
|
|
)
|
|
@format_options
|
|
@click.pass_context
|
|
def shell_order(
|
|
ctx, fmt_name=None, fmt_fields=None, fmt_sort=None, verbose=None, reverse=False
|
|
):
|
|
"Shell loading order"
|
|
|
|
columns = ["order", "name"]
|
|
data = []
|
|
services, _ = ctx.obj.app.catalog.services.get_loading_order(reverse=reverse)
|
|
for index, name in enumerate(services):
|
|
# table.add_row(str(index), name)
|
|
data.append([str(index), name])
|
|
|
|
# Render data
|
|
ctx.obj.render_list(
|
|
data,
|
|
columns,
|
|
conf={
|
|
"table_title": f"Services commands",
|
|
"fmt_name": fmt_name,
|
|
"fmt_sort": fmt_sort,
|
|
"fmt_fields": fmt_fields,
|
|
},
|
|
)
|
|
|
|
|
|
|
|
|
|
@cli__shell.command("enable")
|
|
@click.option(
|
|
"skip_start", "--no-start", "-b", is_flag=True, show_default=False, default=False, help="Disable background process start"
|
|
)
|
|
@click.pass_context
|
|
def shell_enable(ctx, skip_start=True, verbose=None):
|
|
"Enable identity in shell"
|
|
|
|
app = ctx.obj.app
|
|
print(app.shell_enable(run_start=not skip_start))
|
|
|
|
|
|
|
|
@cli__shell.command("disable")
|
|
@click.option(
|
|
"run_stop", "--kill", "-k", is_flag=True, show_default=False, default=False, help="Kill background process on leave"
|
|
)
|
|
@click.pass_context
|
|
def shell_disable(ctx, run_stop=False, verbose=None):
|
|
"Disable identity in shell"
|
|
|
|
ret = ctx.obj.app.shell_disable(run_stop=run_stop)
|
|
print(ret)
|
|
|
|
|
|
@cli__shell.command("kill")
|
|
@click.option(
|
|
"all_idents", "--all", "-a", is_flag=True, show_default=False, default=False, help="Kill on all users"
|
|
)
|
|
@click.pass_context
|
|
def shell_kill(ctx, all_idents=False, verbose=None):
|
|
"Disable identity in shell"
|
|
|
|
ret = []
|
|
|
|
app = ctx.obj.app
|
|
if not all_idents:
|
|
ret.append(app.shell_enable(run_start=False))
|
|
ret.append(app.shell_disable(run_stop=True))
|
|
|
|
else:
|
|
for ident in app.idents.names():
|
|
cli_logger.warning(f"Kill ident {ident}")
|
|
app.init_ident(ident)
|
|
ret.append(app.shell_enable(run_start=False))
|
|
ret.append(app.shell_disable(run_stop=True))
|
|
|
|
print('\n'.join(ret))
|
|
|
|
|
|
|
|
@cli__shell.command("switch")
|
|
@click.argument("ident", required=False)
|
|
# @format_options
|
|
@click.option(
|
|
"skip_start", "--no-start", "-b", is_flag=True, show_default=False, default=False, help="Disable background process start"
|
|
)
|
|
@click.option(
|
|
"run_stop", "--kill", "-k", is_flag=True, show_default=False, default=False, help="Kill background process on leave"
|
|
)
|
|
|
|
@click.pass_context
|
|
def shell_switch(ctx, ident="", run_stop=False, skip_start=True, verbose=None):
|
|
"Enable identity in shell"
|
|
|
|
app = ctx.obj.app
|
|
|
|
src_ident = app.catalog.ident.name
|
|
dst_ident = ident
|
|
|
|
|
|
if src_ident == dst_ident:
|
|
print (">&2 echo 'No need to change'")
|
|
else:
|
|
ret = []
|
|
|
|
# Disable existing
|
|
# logger.info(f"Disabling ident: {app.ident_name}")
|
|
ret.append(app.shell_disable(run_stop=run_stop))
|
|
|
|
# Enable new
|
|
# logger.info(f"Enabling ident: {ident}")
|
|
app.init_ident(dst_ident)
|
|
ret.append(app.shell_enable(run_start=not skip_start))
|
|
|
|
# Output
|
|
print('\n'.join(ret))
|
|
|
|
|
|
|
|
|
|
|
|
class TmpGroup(click.Group):
|
|
def format_help(self, ctx, formatter):
|
|
|
|
val = "List of available commands!"
|
|
formatter.write(val)
|
|
|
|
|
|
|
|
@cli_app.command("run",
|
|
# cls=TmpGroup,
|
|
context_settings=dict(
|
|
ignore_unknown_options=True,
|
|
allow_extra_args=True,
|
|
help_option_names=[]
|
|
)
|
|
)
|
|
@click.argument('cmd_params', nargs=-1, type=click.UNPROCESSED)
|
|
@click.pass_context
|
|
def cli_app_run(ctx, cmd_params):
|
|
|
|
# print("Will run cmd:", cmd_params)
|
|
|
|
# Check first help parameter only
|
|
show_help = False
|
|
show_list = False
|
|
for idx, param in enumerate(cmd_params):
|
|
if idx == 0:
|
|
if param in DEFAULT_HELP_OPTIONS:
|
|
show_help = True
|
|
if param in ["list", "ls"]:
|
|
show_list = True
|
|
|
|
if show_help:
|
|
ctx = click.get_current_context()
|
|
click.echo(ctx.get_help())
|
|
return
|
|
|
|
|
|
# Get instanciated app
|
|
app = ctx.obj.app
|
|
|
|
if show_list:
|
|
ret = app.catalog.services.list_cmds()
|
|
data = [[x.name, x.desc] for x in ret]
|
|
|
|
|
|
# Render data
|
|
ctx.obj.render_list(
|
|
data,
|
|
["name", "desc"],
|
|
conf={
|
|
"table_title": f"Services commands",
|
|
# "fmt_name": fmt_name,
|
|
# "fmt_sort": fmt_sort,
|
|
# "fmt_fields": fmt_fields,
|
|
},
|
|
)
|
|
|
|
return
|
|
|
|
# Run the output
|
|
ret = app.catalog.run_svc_cmd(cmd=cmd_params)
|
|
print (ret)
|
|
# pprint (ret, expand_all=True)
|
|
|
|
|
|
|
|
# @cli_app.command("run2",
|
|
# context_settings=dict(
|
|
# ignore_unknown_options=True,
|
|
# allow_extra_args=True,
|
|
# )
|
|
# )
|
|
# @click.pass_context
|
|
# def cli_app_run(ctx):
|
|
# print("Hello world")
|
|
# pprint (ctx.args)
|
|
|
|
# return
|
|
|
|
|
|
@cli__shell.command("install")
|
|
@click.argument("shell",
|
|
required=False,
|
|
default=DEFAULT_SHELL)
|
|
@click.pass_context
|
|
def shell_install(ctx, shell=None, verbose=None):
|
|
"Install iam in your shell"
|
|
|
|
ret = ctx.obj.app.shell_install(shell=shell)
|
|
|
|
# print("-- %< --" * 8)
|
|
print(ret)
|
|
# print("-- %< --" * 8)
|
|
|
|
|
|
|
|
# Exception handler
|
|
# ===============================
|
|
def clean_terminate(err):
|
|
"Terminate nicely the program depending the exception"
|
|
|
|
# Choose dead end way
|
|
if APP_EXCEPTION is not None and isinstance(err, APP_EXCEPTION):
|
|
err_name = err.__class__.__name__
|
|
# logger.error(err)
|
|
logger.critical("%s: %s" % (err_name, err))
|
|
sys.exit(1)
|
|
|
|
|
|
# Core application executor
|
|
# ===============================
|
|
|
|
|
|
def run():
|
|
"Return a MyApp App instance"
|
|
|
|
try:
|
|
cli_app(**CONTEXT_SETTINGS)
|
|
|
|
# pylint: disable=broad-except
|
|
except Exception as err:
|
|
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 __name__ == "__main__":
|
|
run()
|