From 0e0e7fece180f65d809e6339b073cdb88c0ffeff Mon Sep 17 00:00:00 2001 From: Ivan Ogasawara Date: Thu, 10 Oct 2024 08:29:16 -0400 Subject: [PATCH] fix: Refactor the interface for the plugins/extensions, move the main commands to the compose group (#127) --- .github/workflows/docs.yaml | 2 +- .github/workflows/main.yaml | 1 - .makim.yaml | 148 ++++----- .sugar.yaml | 10 +- README.md | 6 +- docs/index.md | 6 +- src/sugar/cli.py | 232 +++++++------- src/sugar/core.py | 17 +- src/sugar/{plugins => extensions}/__init__.py | 0 src/sugar/{plugins => extensions}/base.py | 300 +++--------------- src/sugar/extensions/compose.py | 192 +++++++++++ .../ext.py => extensions/compose_ext.py} | 32 +- src/sugar/{plugins => extensions}/stats.py | 21 +- test.yaml | 2 +- tests/containers/.services.sugar.yaml | 4 +- tests/test_containers_sugar.py | 14 +- 16 files changed, 487 insertions(+), 500 deletions(-) rename src/sugar/{plugins => extensions}/__init__.py (100%) rename src/sugar/{plugins => extensions}/base.py (55%) create mode 100644 src/sugar/extensions/compose.py rename src/sugar/{plugins/ext.py => extensions/compose_ext.py} (54%) rename src/sugar/{plugins => extensions}/stats.py (94%) diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index a5150be..25d1a7c 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -30,7 +30,7 @@ jobs: activate-environment: sugar auto-update-conda: true conda-solver: libmamba - python-version: "3.8" + python-version: "3.9" - name: Install deps run: | diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 8f93861..9eb2c3f 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -39,7 +39,6 @@ jobs: strategy: matrix: python_version: - - "3.8" - "3.9" - "3.10" - "3.11" diff --git a/.makim.yaml b/.makim.yaml index f05bea0..77a0626 100644 --- a/.makim.yaml +++ b/.makim.yaml @@ -64,61 +64,61 @@ groups: dependencies: - task: docker.killall run: | - sugar build --verbose - sugar build --verbose --group group1 --all - sugar build --verbose --group group1 - sugar build --verbose --group group1 --services service1-1 - sugar pull --verbose --group group1 --all - sugar pull --verbose --group group1 - sugar pull --verbose --group group1 --services service1-1 - sugar ext start --verbose --group group1 --all --options -d - sugar ext restart --verbose --group group1 --all --options -d - sugar exec --verbose --group group1 --service service1-1 --options -T --cmd env - sugar stop --verbose --group group1 --all - sugar run --verbose --group group1 --service service1-1 --options -T --cmd env - sugar down --verbose --group group1 + sugar compose build --verbose + sugar compose build --verbose --group group1 --all + sugar compose build --verbose --group group1 + sugar compose build --verbose --group group1 --services service1-1 + sugar compose pull --verbose --group group1 --all + sugar compose pull --verbose --group group1 + sugar compose pull --verbose --group group1 --services service1-1 + sugar compose-ext start --verbose --group group1 --all --options -d + sugar compose-ext restart --verbose --group group1 --all --options -d + sugar compose exec --verbose --group group1 --service service1-1 --options -T --cmd env + sugar compose stop --verbose --group group1 --all + sugar compose run --verbose --group group1 --service service1-1 --options -T --cmd env + sugar compose down --verbose --group group1 smoke-2: help: Run smoke tests for group 2 dependencies: - task: docker.killall run: | - sugar build --verbose --group group2 --all - sugar build --verbose --group group2 - sugar build --verbose --group group2 --services service2-1 - sugar pull --verbose --group group2 --all - sugar pull --verbose --group group2 - sugar pull --verbose --group group2 --services service2-1 - sugar ext start --verbose --group group2 --all --options -d - sugar ext restart --verbose --group group2 --all --options -d - sugar exec --verbose --group group2 --service service2-1 --options -T --cmd env - sugar stop --verbose --group group2 --all - sugar run --verbose --group group2 --service service2-1 --options -T --cmd env - sugar down --verbose --group group2 + sugar compose build --verbose --group group2 --all + sugar compose build --verbose --group group2 + sugar compose build --verbose --group group2 --services service2-1 + sugar compose pull --verbose --group group2 --all + sugar compose pull --verbose --group group2 + sugar compose pull --verbose --group group2 --services service2-1 + sugar compose-ext start --verbose --group group2 --all --options -d + sugar compose-ext restart --verbose --group group2 --all --options -d + sugar compose exec --verbose --group group2 --service service2-1 --options -T --cmd env + sugar compose stop --verbose --group group2 --all + sugar compose run --verbose --group group2 --service service2-1 --options -T --cmd env + sugar compose down --verbose --group group2 smoke-services: help: dependencies: - task: docker.killall run: | - sugar build --verbose --config-file tests/containers/.services.sugar.yaml + sugar compose build --verbose --config-file tests/containers/.services.sugar.yaml smoke-mix: help: Run smoke tests for group mix dependencies: - task: docker.killall run: | - sugar build --verbose --group group-mix --all - sugar build --verbose --group group-mix - sugar build --verbose --group group-mix --services service1-1,service2-1 - sugar pull --verbose --group group-mix --all - sugar pull --verbose --group group-mix - sugar pull --verbose --group group-mix --services service1-1,service2-1 - sugar ext start --verbose --group group-mix --all --options -d - sugar ext restart --verbose --group group-mix --all --options -d - sugar exec --verbose --group group-mix --service service2-1 --options -T --cmd env - sugar stop --verbose --group group-mix --all - sugar run --verbose --group group-mix --service service2-1 --options -T --cmd env - sugar down --verbose --group group-mix + sugar compose build --verbose --group group-mix --all + sugar compose build --verbose --group group-mix + sugar compose build --verbose --group group-mix --services service1-1,service2-1 + sugar compose pull --verbose --group group-mix --all + sugar compose pull --verbose --group group-mix + sugar compose pull --verbose --group group-mix --services service1-1,service2-1 + sugar compose-ext start --verbose --group group-mix --all --options -d + sugar compose-ext restart --verbose --group group-mix --all --options -d + sugar compose exec --verbose --group group-mix --service service2-1 --options -T --cmd env + sugar compose stop --verbose --group group-mix --all + sugar compose run --verbose --group group-mix --service service2-1 --options -T --cmd env + sugar compose down --verbose --group group-mix smoke-main: help: Run smoke tests for group main @@ -126,25 +126,25 @@ groups: - task: docker.killall run: | # general tests main profile/plugins - sugar build --verbose --group group1 - sugar config --verbose --group group1 - sugar create --verbose --group group1 - sugar ext start --verbose --group group1 --options -d - sugar ext restart --verbose --group group1 --options -d - sugar exec --verbose --group group1 --service service1-1 --options -T --cmd env - sugar images --verbose --group group1 - sugar logs --verbose --group group1 + sugar compose build --verbose --group group1 + sugar compose config --verbose --group group1 + sugar compose create --verbose --group group1 + sugar compose-ext start --verbose --group group1 --options -d + sugar compose-ext restart --verbose --group group1 --options -d + sugar compose exec --verbose --group group1 --service service1-1 --options -T --cmd env + sugar compose images --verbose --group group1 + sugar compose logs --verbose --group group1 # port is not complete supported - # sugar port --verbose --group group1 --service service1-1 - sugar ps --verbose --group group1 - sugar pull --verbose --group group1 - sugar push --verbose --group group1 - sugar run --verbose --group group1 --service service1-1 --options -T --cmd env - sugar top --verbose --group group1 - sugar up --verbose --group group1 --options -d - sugar version --verbose + # sugar compose port --verbose --group group1 --service service1-1 + sugar compose ps --verbose --group group1 + sugar compose pull --verbose --group group1 + sugar compose push --verbose --group group1 + sugar compose run --verbose --group group1 --service service1-1 --options -T --cmd env + sugar compose top --verbose --group group1 + sugar compose up --verbose --group group1 --options -d + sugar compose version --verbose # port is not complete supported - # sugar events --verbose --group group1 --service service1-1 --options --json --dry-run + # sugar compose events --verbose --group group1 --service service1-1 --options --json --dry-run smoke-defaults: help: Run smoke tests for group defaults @@ -153,36 +153,36 @@ groups: run: | export SUGAR_PROJECT_NAME="test-`python -c 'from uuid import uuid4; print(uuid4().hex[:7])'`" echo $SUGAR_PROJECT_NAME - sugar build --verbose --group group-defaults - sugar ext start --verbose --group group-defaults --options -d - sugar ext restart --verbose --group group-defaults --options -d + sugar compose build --verbose --group group-defaults + sugar compose-ext start --verbose --group group-defaults --options -d + sugar compose-ext restart --verbose --group group-defaults --options -d docker ps|grep $SUGAR_PROJECT_NAME - sugar ext stop --verbose --group group-defaults + sugar compose-ext stop --verbose --group group-defaults smoke-final: help: Run final smoke tests dependencies: - task: docker.killall run: | - sugar ext restart --verbose --group group-defaults --options -d - sugar pause --verbose --group group1 - sugar unpause --verbose --group group1 - sugar kill --verbose --group group1 - sugar stop --verbose --group group1 - sugar rm --verbose --group group1 --options --force - sugar down --verbose --group group1 + sugar compose-ext restart --verbose --group group-defaults --options -d + sugar compose pause --verbose --group group1 + sugar compose unpause --verbose --group group1 + sugar compose kill --verbose --group group1 + sugar compose stop --verbose --group group1 + sugar compose rm --verbose --group group1 --options --force + sugar compose down --verbose --group group1 smoke-experimental: help: Run simple text for experimental commands dependencies: - task: docker.killall run: | - sugar attach --options --help - sugar cp --options --help - sugar ls --options --help - sugar scale --options --help - sugar wait --options --help - sugar watch --options --help + sugar compose attach --options --help + sugar compose cp --options --help + sugar compose ls --options --help + sugar compose scale --options --help + sugar compose wait --options --help + sugar compose watch --options --help smoke: help: Run final smoke tests @@ -197,8 +197,8 @@ groups: - task: tests.smoke-services - task: tests.smoke-experimental run: | - sugar --help - sugar --version + sugar compose --help + sugar compose version docker: help: Commands for docker diff --git a/.sugar.yaml b/.sugar.yaml index 6bae818..8950db4 100644 --- a/.sugar.yaml +++ b/.sugar.yaml @@ -1,5 +1,5 @@ version: 1.0 -compose-app: docker-compose +backend: docker-compose env-file: .env defaults: group: ${{ env.SUGAR_GROUP }} @@ -7,7 +7,7 @@ defaults: groups: group1: project-name: project1 # optional - compose-path: tests/containers/group1/compose.yaml + config-path: tests/containers/group1/compose.yaml env-file: .env services: default: service1-1,service1-3 @@ -18,7 +18,7 @@ groups: group2: project-name: null # optional - compose-path: tests/containers/group2/compose.yaml + config-path: tests/containers/group2/compose.yaml env-file: .env services: default: null @@ -28,7 +28,7 @@ groups: group-mix: project-name: null # optional - compose-path: + config-path: - tests/containers/group1/compose.yaml - tests/containers/group2/compose.yaml env-file: .env @@ -41,7 +41,7 @@ groups: - name: service2-2 group-defaults: - compose-path: + config-path: - tests/containers/group1/compose.yaml env-file: .env services: diff --git a/README.md b/README.md index 34b09c5..38374d7 100644 --- a/README.md +++ b/README.md @@ -76,13 +76,13 @@ project. This is an example of a configuration file: ```yaml version: 1.0 -compose-app: docker compose +backend: docker compose default: group: ${{ env.ENV }} groups: group1: project-name: project1 - compose-path: + config-path: - containers/tests/group1/compose.yaml env-file: .env services: @@ -93,7 +93,7 @@ groups: - name: service3 group2: project-name: null - compose-path: containers/tests/group2/compose.yaml + config-path: containers/tests/group2/compose.yaml env-file: .env services: available: diff --git a/docs/index.md b/docs/index.md index a3491da..c33eb26 100644 --- a/docs/index.md +++ b/docs/index.md @@ -76,13 +76,13 @@ project. This is an example of a configuration file: ```yaml version: 1.0 -compose-app: docker compose +backend: docker compose default: group: ${{ "${{ env.ENV }}" }} groups: group1: project-name: project1 - compose-path: + config-path: - containers/tests/group1/compose.yaml env-file: .env services: @@ -95,7 +95,7 @@ groups: - name: service3 group2: project-name: null - compose-path: containers/tests/group2/compose.yaml + config-path: containers/tests/group2/compose.yaml env-file: .env services: # default: null diff --git a/src/sugar/cli.py b/src/sugar/cli.py index ee6b22d..a1f5617 100644 --- a/src/sugar/cli.py +++ b/src/sugar/cli.py @@ -43,7 +43,7 @@ def extract_options_and_cmd_args() -> tuple[list[str], list[str]]: if options_sep_idx is None and cmd_sep_idx is None: return [], [] - # check if --pre-args or --post-args are the last ones in the command line + # check if --options or --cmd are the last ones in the command line first_sep_idx = min( [(options_sep_idx or total_args), (cmd_sep_idx or total_args)] ) @@ -82,15 +82,19 @@ def extract_options_and_cmd_args() -> tuple[list[str], list[str]]: return options_args, cmd_args -def create_main_group(sugar_app: typer.Typer) -> None: +def create_compose_group(sugar_app: typer.Typer) -> None: """ Create the main plugin command group. Also add the commands to sugar app. """ + compose_group = typer.Typer( + help='Use the `compose` extension.', + invoke_without_command=True, + ) # -- Main commands -- - @sugar_app.command() + @compose_group.command() def attach( ctx: typer.Context, service_group: str = Option( @@ -105,7 +109,7 @@ def attach( options: str = Option( None, help=( - 'Specify the options for docker-compose command. ' + 'Specify the options for the backend command. ' 'E.g.: --options -d' ), ), @@ -131,7 +135,7 @@ def attach( Note: This is an experimental feature. """ args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'attach' if verbose or flags_state['verbose']: @@ -141,7 +145,7 @@ def attach( Sugar(args, options_args=opts_args).run() - @sugar_app.command() + @compose_group.command() def build( ctx: typer.Context, service_group: str = Option( @@ -160,7 +164,7 @@ def build( options: str = Option( None, help=( - 'Specify the options for docker-compose command. ' + 'Specify the options for the backend command. ' 'E.g.: --options -d' ), ), @@ -184,7 +188,7 @@ def build( ) -> None: """Build or rebuild services.""" args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'build' if verbose or flags_state['verbose']: @@ -195,7 +199,7 @@ def build( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @sugar_app.command() + @compose_group.command() def config( ctx: typer.Context, service_group: str = Option( @@ -213,7 +217,7 @@ def config( ), options: str = Option( None, - help='Specify the options for docker-compose command. \ + help='Specify the options for the backend command. \ E.g.: --options -d', ), config_file: str = Option( @@ -231,7 +235,7 @@ def config( ) -> None: """Parse, resolve and render compose file in canonical format.""" args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'config' if verbose or flags_state['verbose']: @@ -242,7 +246,7 @@ def config( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @sugar_app.command() + @compose_group.command() def cp( ctx: typer.Context, service_group: str = Option( @@ -254,7 +258,7 @@ def cp( options: str = Option( None, help=( - 'Specify the options for docker-compose command. ' + 'Specify the options for the backend command. ' 'E.g.: --options -d' ), ), @@ -277,7 +281,7 @@ def cp( Note: This is an experimental feature. """ args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'cp' if verbose or flags_state['verbose']: @@ -287,7 +291,7 @@ def cp( Sugar(args, options_args=opts_args).run() - @sugar_app.command() + @compose_group.command() def create( ctx: typer.Context, service_group: str = Option( @@ -303,7 +307,7 @@ def create( ), options: str = Option( None, - help='Specify the options for docker-compose command. \ + help='Specify the options for the backend command. \ E.g.: --options -d', ), all: bool = Option( @@ -326,7 +330,7 @@ def create( ) -> None: """Create containers for a service.""" args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'create' if verbose or flags_state['verbose']: @@ -337,7 +341,7 @@ def create( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @sugar_app.command() + @compose_group.command() def down( ctx: typer.Context, service_group: str = Option( @@ -348,7 +352,7 @@ def down( ), options: str = Option( None, - help='Specify the options for docker-compose command. \ + help='Specify the options for the backend command. \ E.g.: --options -d', ), config_file: str = Option( @@ -366,7 +370,7 @@ def down( ) -> None: """Stop and remove containers, networks.""" args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'down' if verbose or flags_state['verbose']: @@ -377,7 +381,7 @@ def down( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @sugar_app.command() + @compose_group.command() def events( ctx: typer.Context, service_group: str = Option( @@ -396,7 +400,7 @@ def events( options: str = Option( None, help=( - 'Specify the options for docker-compose command. ' + 'Specify the options for the backend command. ' 'E.g.: --options -d' ), ), @@ -413,7 +417,7 @@ def events( ) -> None: """Receive real time events from containers.""" args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'events' cmd_args: list[Any] = opt_state['cmd'] @@ -421,8 +425,8 @@ def events( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @sugar_app.command(name='exec') - def exec_command( + @compose_group.command(name='exec') + def exec( ctx: typer.Context, service_group: str = Option( None, @@ -435,7 +439,7 @@ def exec_command( ), options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), config_file: str = Option( @@ -458,7 +462,7 @@ def exec_command( ) -> None: """Execute a command in a running container.""" args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'exec' if verbose or flags_state['verbose']: @@ -469,7 +473,7 @@ def exec_command( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @sugar_app.command() + @compose_group.command() def images( ctx: typer.Context, service_group: str = Option( @@ -485,7 +489,7 @@ def images( ), options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), all: bool = Option( @@ -508,7 +512,7 @@ def images( ) -> None: """List images used by the created containers.""" args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'images' if verbose or flags_state['verbose']: @@ -519,7 +523,7 @@ def images( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @sugar_app.command() + @compose_group.command() def kill( ctx: typer.Context, service_group: str = Option( @@ -535,7 +539,7 @@ def kill( ), options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), all: bool = Option( @@ -558,7 +562,7 @@ def kill( ) -> None: """Force stop service containers.""" args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'kill' if verbose or flags_state['verbose']: @@ -569,7 +573,7 @@ def kill( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @sugar_app.command() + @compose_group.command() def logs( ctx: typer.Context, service_group: str = Option( @@ -585,7 +589,7 @@ def logs( ), options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), all: bool = Option( @@ -608,7 +612,7 @@ def logs( ) -> None: """View output from containers.""" args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'logs' if verbose or flags_state['verbose']: @@ -619,7 +623,7 @@ def logs( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @sugar_app.command() + @compose_group.command() def ls( ctx: typer.Context, service_group: str = Option( @@ -630,7 +634,7 @@ def ls( ), options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), config_file: str = Option( @@ -652,7 +656,7 @@ def ls( Note: This is an experimental feature. """ args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'ls' if verbose or flags_state['verbose']: @@ -662,7 +666,7 @@ def ls( Sugar(args, options_args=opts_args).run() - @sugar_app.command() + @compose_group.command() def pause( ctx: typer.Context, service_group: str = Option( @@ -678,7 +682,7 @@ def pause( ), options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), all: bool = Option( @@ -701,7 +705,7 @@ def pause( ) -> None: """Pause services.""" args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'pause' if verbose or flags_state['verbose']: @@ -712,7 +716,7 @@ def pause( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @sugar_app.command() + @compose_group.command() def port( ctx: typer.Context, service: str = Option( @@ -720,7 +724,7 @@ def port( ), options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), config_file: str = Option( @@ -731,7 +735,7 @@ def port( ) -> None: """Print the public port for a port binding.""" args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'port' cmd_args: list[Any] = opt_state['cmd'] @@ -739,7 +743,7 @@ def port( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @sugar_app.command() + @compose_group.command() def ps( ctx: typer.Context, service_group: str = Option( @@ -755,7 +759,7 @@ def ps( ), options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), all: bool = Option( @@ -778,7 +782,7 @@ def ps( ) -> None: """List containers.""" args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'ps' if verbose or flags_state['verbose']: @@ -789,7 +793,7 @@ def ps( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @sugar_app.command() + @compose_group.command() def pull( ctx: typer.Context, service_group: str = Option( @@ -805,7 +809,7 @@ def pull( ), options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), all: bool = Option( @@ -828,7 +832,7 @@ def pull( ) -> None: """Pull service images.""" args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'pull' if verbose or flags_state['verbose']: @@ -839,7 +843,7 @@ def pull( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @sugar_app.command() + @compose_group.command() def push( ctx: typer.Context, service_group: str = Option( @@ -855,7 +859,7 @@ def push( ), options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), all: bool = Option( @@ -878,7 +882,7 @@ def push( ) -> None: """Push service images.""" args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'push' if verbose or flags_state['verbose']: @@ -889,7 +893,7 @@ def push( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @sugar_app.command() + @compose_group.command() def restart( ctx: typer.Context, service_group: str = Option( @@ -905,7 +909,7 @@ def restart( ), options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), all: bool = Option( @@ -928,7 +932,7 @@ def restart( ) -> None: """Restart service containers.""" args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'restart' if verbose or flags_state['verbose']: @@ -939,7 +943,7 @@ def restart( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @sugar_app.command() + @compose_group.command() def rm( ctx: typer.Context, service_group: str = Option( @@ -985,7 +989,7 @@ def rm( Any data which is not in a volume will be lost. """ args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'rm' cmd_args: list[Any] = opt_state['cmd'] @@ -993,7 +997,7 @@ def rm( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @sugar_app.command() + @compose_group.command() def run( ctx: typer.Context, service_group: str = Option( @@ -1007,7 +1011,7 @@ def run( ), options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), config_file: str = Option( @@ -1030,7 +1034,7 @@ def run( ) -> None: """Run a one-off command on a service.""" args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'run' if verbose or flags_state['verbose']: @@ -1041,7 +1045,7 @@ def run( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @sugar_app.command() + @compose_group.command() def scale( ctx: typer.Context, service_group: str = Option( @@ -1052,7 +1056,7 @@ def scale( ), options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), config_file: str = Option( @@ -1074,7 +1078,7 @@ def scale( Note: This is an experimental feature. """ args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'scale' if verbose or flags_state['verbose']: @@ -1084,7 +1088,7 @@ def scale( Sugar(args, options_args=opts_args).run() - @sugar_app.command() + @compose_group.command() def start( ctx: typer.Context, service_group: str = Option( @@ -1100,7 +1104,7 @@ def start( ), options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), all: bool = Option( @@ -1123,7 +1127,7 @@ def start( ) -> None: """Start services.""" args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'start' if verbose or flags_state['verbose']: @@ -1134,7 +1138,7 @@ def start( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @sugar_app.command() + @compose_group.command() def stop( ctx: typer.Context, service_group: str = Option( @@ -1150,7 +1154,7 @@ def stop( ), options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), all: bool = Option( @@ -1173,7 +1177,7 @@ def stop( ) -> None: """Stop services.""" args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'stop' if verbose or flags_state['verbose']: @@ -1184,7 +1188,7 @@ def stop( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @sugar_app.command() + @compose_group.command() def top( ctx: typer.Context, service_group: str = Option( @@ -1200,7 +1204,7 @@ def top( ), options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), all: bool = Option( @@ -1223,7 +1227,7 @@ def top( ) -> None: """Display the running processes.""" args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'top' if verbose or flags_state['verbose']: @@ -1234,7 +1238,7 @@ def top( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @sugar_app.command() + @compose_group.command() def unpause( ctx: typer.Context, service_group: str = Option( @@ -1250,7 +1254,7 @@ def unpause( ), options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), all: bool = Option( @@ -1273,7 +1277,7 @@ def unpause( ) -> None: """Unpause services.""" args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'unpause' if verbose or flags_state['verbose']: @@ -1284,7 +1288,7 @@ def unpause( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @sugar_app.command() + @compose_group.command() def up( ctx: typer.Context, service_group: str = Option( @@ -1300,7 +1304,7 @@ def up( ), options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), all: bool = Option( @@ -1323,7 +1327,7 @@ def up( ) -> None: """Create and start containers.""" args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'up' if verbose or flags_state['verbose']: @@ -1334,12 +1338,12 @@ def up( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @sugar_app.command() + @compose_group.command() def version( ctx: typer.Context, options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), config_file: str = Option( @@ -1357,7 +1361,7 @@ def version( ) -> None: """Show the Docker Compose version information.""" args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'version' if verbose or flags_state['verbose']: @@ -1368,7 +1372,7 @@ def version( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @sugar_app.command() + @compose_group.command() def wait( ctx: typer.Context, service_group: str = Option( @@ -1384,7 +1388,7 @@ def wait( ), options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), all: bool = Option( @@ -1411,7 +1415,7 @@ def wait( Note: This is an experimental feature. """ args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'wait' if verbose or flags_state['verbose']: @@ -1421,7 +1425,7 @@ def wait( Sugar(args, options_args=opts_args).run() - @sugar_app.command() + @compose_group.command() def watch( ctx: typer.Context, service_group: str = Option( @@ -1437,7 +1441,7 @@ def watch( ), options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), all: bool = Option( @@ -1467,7 +1471,7 @@ def watch( Note: This is an experimental feature. """ args = ctx.params - args['plugin'] = 'main' + args['plugin'] = 'compose' args['action'] = 'watch' if verbose or flags_state['verbose']: @@ -1477,21 +1481,25 @@ def watch( Sugar(args, options_args=opts_args).run() + sugar_app.add_typer( + compose_group, name='compose', rich_help_panel='Extensions' + ) + -def create_ext_group(sugar_app: typer.Typer) -> None: +def create_compose_ext_group(sugar_app: typer.Typer) -> None: """ Create a command group for ext plugin. The function also associate the group to sugar app. """ - ext_group = typer.Typer( + compose_ext_group = typer.Typer( help='Use the `ext` plugin.', invoke_without_command=True, ) # -- Ext Commands - @ext_group.command(name='get-ip') + @compose_ext_group.command(name='get-ip') def get_ip( ctx: typer.Context, service_group: str = Option( @@ -1511,7 +1519,7 @@ def get_ip( ) -> None: """Get the IP for given service (NOT IMPLEMENTED YET).""" args = ctx.params - args['plugin'] = 'ext' + args['plugin'] = 'compose-ext' args['action'] = 'get-ip' cmd_args: list[Any] = opt_state['cmd'] @@ -1519,7 +1527,7 @@ def get_ip( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @ext_group.command(name='start') + @compose_ext_group.command(name='start') def ext_start( ctx: typer.Context, service_group: str = Option( @@ -1535,7 +1543,7 @@ def ext_start( ), options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), all: bool = Option( @@ -1558,7 +1566,7 @@ def ext_start( ) -> None: """Run `up` main command (alias).""" args = ctx.params - args['plugin'] = 'ext' + args['plugin'] = 'compose-ext' args['action'] = 'start' if verbose or flags_state['verbose']: @@ -1569,7 +1577,7 @@ def ext_start( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @ext_group.command(name='stop') + @compose_ext_group.command(name='stop') def ext_stop( ctx: typer.Context, service_group: str = Option( @@ -1585,7 +1593,7 @@ def ext_stop( ), options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), all: bool = Option( @@ -1608,7 +1616,7 @@ def ext_stop( ) -> None: """Run the main stop command (alias).""" args = ctx.params - args['plugin'] = 'ext' + args['plugin'] = 'compose-ext' args['action'] = 'stop' if verbose or flags_state['verbose']: @@ -1619,7 +1627,7 @@ def ext_stop( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @ext_group.command(name='restart') + @compose_ext_group.command(name='restart') def ext_restart( ctx: typer.Context, service_group: str = Option( @@ -1635,7 +1643,7 @@ def ext_restart( ), options: str = Option( None, - help='Specify the options for docker-compose command.\ + help='Specify the options for the backend command.\ E.g.: --options -d', ), all: bool = Option( @@ -1658,7 +1666,7 @@ def ext_restart( ) -> None: """Run `down` and `up` sequentially.""" args = ctx.params - args['plugin'] = 'ext' + args['plugin'] = 'compose-ext' args['action'] = 'restart' if verbose or flags_state['verbose']: @@ -1669,7 +1677,7 @@ def ext_restart( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - @ext_group.command() + @compose_ext_group.command() def wait( ctx: typer.Context, service_group: str = Option( @@ -1696,7 +1704,7 @@ def wait( ) -> None: """Wait until the service are healthy (NOT IMPLEMENTED YET).""" args = ctx.params - args['plugin'] = 'ext' + args['plugin'] = 'compose-ext' args['action'] = 'wait' if verbose or flags_state['verbose']: @@ -1707,7 +1715,9 @@ def wait( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - sugar_app.add_typer(ext_group, name='ext', rich_help_panel='Plugins') + sugar_app.add_typer( + compose_ext_group, name='compose-ext', rich_help_panel='Extensions' + ) def create_stats_group(sugar_app: typer.Typer) -> None: @@ -1753,11 +1763,13 @@ def plot( Sugar(args, options_args=opts_args, cmd_args=cmd_args).run() - sugar_app.add_typer(stats_group, name='stats', rich_help_panel='Plugins') + sugar_app.add_typer( + stats_group, name='stats', rich_help_panel='Extensions' + ) def create_app() -> typer.Typer: - """Create app function to instantiate the typer app dinamically.""" + """Create app function to instantiate the typer app dynamically.""" options_args, cmd_args = extract_options_and_cmd_args() opt_state['options'] = options_args opt_state['cmd'] = cmd_args @@ -1778,8 +1790,8 @@ def create_app() -> typer.Typer: # -- Add typer groups -- - create_main_group(sugar_app) - create_ext_group(sugar_app) + create_compose_group(sugar_app) + create_compose_ext_group(sugar_app) create_stats_group(sugar_app) # -- Callbacks -- @@ -1832,5 +1844,5 @@ def main( app = create_app() -if __name__ == '__main__': +if __name__ == '__compose__': app() diff --git a/src/sugar/core.py b/src/sugar/core.py index 8aa0834..ff69f8a 100644 --- a/src/sugar/core.py +++ b/src/sugar/core.py @@ -6,12 +6,13 @@ from typing import Optional, Type, cast +from sugar.extensions.base import SugarBase +from sugar.extensions.compose import SugarCompose +from sugar.extensions.compose_ext import SugarComposeExt from sugar.logs import SugarErrorType, SugarLogs -from sugar.plugins.base import SugarBase, SugarDockerCompose -from sugar.plugins.ext import SugarExt try: - from sugar.plugins.stats import SugarStats + from sugar.extensions.stats import SugarStats except ImportError: # SugarStats is optional (extras=tui) SugarStats = cast(Optional[Type[SugarBase]], None) # type: ignore @@ -21,8 +22,8 @@ class Sugar(SugarBase): """Sugar main class.""" plugins_definition: dict[str, Type[SugarBase]] = { - 'main': SugarDockerCompose, - 'ext': SugarExt, + 'compose': SugarCompose, + 'compose-ext': SugarComposeExt, **{'stats': SugarStats for i in range(1) if SugarStats is not None}, } plugin: Optional[SugarBase] = None @@ -32,7 +33,7 @@ def __init__( args: dict[str, str], options_args: list[str] = [], cmd_args: list[str] = [], - ): + ) -> None: """Initialize the Sugar object according to the plugin used.""" plugin_name = args.get('plugin', '') @@ -44,7 +45,7 @@ def __init__( and not args.get('action') ): args['action'] = plugin_name - args['plugin'] = 'main' + args['plugin'] = 'compose' # update plugin name plugin_name = args.get('plugin', '') @@ -80,7 +81,7 @@ def get_actions(self) -> list[str]: return actions - def _load_compose_args(self) -> None: + def _load_backend_args(self) -> None: pass def _load_service_names(self) -> None: diff --git a/src/sugar/plugins/__init__.py b/src/sugar/extensions/__init__.py similarity index 100% rename from src/sugar/plugins/__init__.py rename to src/sugar/extensions/__init__.py diff --git a/src/sugar/plugins/base.py b/src/sugar/extensions/base.py similarity index 55% rename from src/sugar/plugins/base.py rename to src/sugar/extensions/base.py index 7e7fcb9..cdeee88 100644 --- a/src/sugar/plugins/base.py +++ b/src/sugar/extensions/base.py @@ -35,8 +35,8 @@ class SugarBase: config: dict[str, Any] = {} # note: it starts with a simple command # it is replaced later in the execution - compose_app: sh.Command = sh.echo - compose_args: list[str] = [] + backend_app: sh.Command = sh.echo + backend_args: list[str] = [] defaults: dict[str, Any] = {} env: dict[str, str] = {} options_args: list[str] = [] @@ -44,6 +44,18 @@ class SugarBase: service_group: dict[str, Any] = {} service_names: list[str] = [] + def __init_subclass__(cls, **kwargs: Any) -> None: + """Initialize the actions list for all the created commands.""" + super().__init_subclass__(**kwargs) + # Ensure each subclass has its own actions list + cls.actions = cls.actions.copy() + prefix = '_cmd_' + prefix_len = len(prefix) + for name, value in cls.__dict__.items(): + if callable(value) and name.startswith(prefix): + action_name = name[prefix_len:] + cls.actions.append(action_name) + def __init__( self, args: dict[str, str], @@ -56,7 +68,7 @@ def __init__( self.cmd_args = deepcopy(cmd_args) self.config_file = self.args.get('config_file', '') self.config: dict[str, Any] = {} - self.compose_args: list[str] = [] + self.backend_args: list[str] = [] self.defaults: dict[str, Any] = {} self.env: dict[str, str] = {} self.service_group: dict[str, Any] = {} @@ -67,12 +79,12 @@ def __init__( self._load_defaults() self._load_root_services() self._verify_args() - self._load_compose_app() - self._load_compose_args() + self._load_backend_app() + self._load_backend_args() self._verify_config() self._load_service_names() - def _call_compose_app_core( + def _call_backend_app_core( self, *args: str, services: list[str] = [], @@ -92,7 +104,7 @@ def _call_compose_app_core( } positional_parameters = ( - self.compose_args + self.backend_args + list(args) + (options_args or self.options_args) + services @@ -100,10 +112,10 @@ def _call_compose_app_core( ) if self.args.get('verbose'): - print('>>>', self.compose_app, *positional_parameters) + print('>>>', self.backend_app, *positional_parameters) print('-' * 80) - p = self.compose_app( + p = self.backend_app( *positional_parameters, **sh_extras, ) @@ -119,12 +131,12 @@ def _call_compose_app_core( f'Process {pid} killed.', SugarErrorType.SH_KEYBOARD_INTERRUPT ) - def _call_compose_app( + def _call_backend_app( self, *args: str, services: list[str] = [], ) -> None: - self._call_compose_app_core( + self._call_backend_app_core( *args, services=services, _out=sys.stdout, @@ -150,7 +162,7 @@ def _load_root_services(self) -> None: self.config['groups'] = { 'main': { 'project-name': services.get('project-name'), - 'compose-path': services.get('compose-path'), + 'config-path': services.get('config-path'), 'env-file': services.get('env-file'), 'services': { 'default': services.get('default'), @@ -226,47 +238,47 @@ def _load_config(self) -> None: SugarErrorType.SUGAR_INVALID_CONFIGURATION, ) - def _load_compose_app(self) -> None: - compose_cmd = self.config.get('compose-app', '') - if compose_cmd.replace(' ', '-') != 'docker-compose': + def _load_backend_app(self) -> None: + backend_cmd = self.config.get('backend', '') + if backend_cmd.replace(' ', '-') != 'docker-compose': SugarLogs.raise_error( - f'"{self.config["compose-app"]}" not supported yet.', + f'"{self.config["backend"]}" not supported yet.', SugarErrorType.SUGAR_COMPOSE_APP_NOT_SUPPORTED, ) - if compose_cmd == 'docker-compose': - self.compose_app = sh.docker_compose + if backend_cmd == 'docker-compose': + self.backend_app = sh.docker_compose return - self.compose_app = sh.docker - self.compose_args.append('compose') + self.backend_app = sh.docker + self.backend_args.append('compose') - def _load_compose_args(self) -> None: + def _load_backend_args(self) -> None: self._filter_service_group() if self.service_group.get('env-file'): - self.compose_args.extend( + self.backend_args.extend( ['--env-file', self.service_group['env-file']] ) - compose_path = [] - compose_path_arg = self.service_group['compose-path'] - if isinstance(compose_path_arg, str): - compose_path.append(compose_path_arg) - elif isinstance(compose_path_arg, list): - compose_path.extend(compose_path_arg) + config_path = [] + backend_path_arg = self.service_group['config-path'] + if isinstance(backend_path_arg, str): + config_path.append(backend_path_arg) + elif isinstance(backend_path_arg, list): + config_path.extend(backend_path_arg) else: SugarLogs.raise_error( - 'The attribute compose-path` just supports the data ' - f'types `string` or `list`, {type(compose_path_arg)} ' + 'The attribute config-path` just supports the data ' + f'types `string` or `list`, {type(backend_path_arg)} ' 'received.', SugarErrorType.SUGAR_INVALID_CONFIGURATION, ) - for p in compose_path: - self.compose_args.extend(['--file', p]) + for p in config_path: + self.backend_args.extend(['--file', p]) if self.service_group.get('project-name'): - self.compose_args.extend( + self.backend_args.extend( ['--project-name', self.service_group['project-name']] ) @@ -355,225 +367,9 @@ def run(self) -> None: 'The given action is not valid.', SugarErrorType.SUGAR_INVALID_PARAMETER, ) - getattr(self, f'_{action.replace("-", "_")}')() + getattr(self, f'_cmd_{action.replace("-", "_")}')() def _version(self) -> None: SugarLogs.print_info(f'Sugar Version: {__version__}') - SugarLogs.print_info(f'Container Program Path: {self.compose_app}') - self._call_compose_app('version', services=[]) - - -class SugarDockerCompose(SugarBase): - """ - SugarDockerCompose provides the docker compose commands. - - This are the commands that is currently provided: - - attach [options] SERVICE - build [options] [SERVICE...] - config [options] [SERVICE...] - cp: - - cp [options] SERVICE:SRC_PATH DEST_PATH|- - - cp [options] SRC_PATH|- SERVICE:DEST_PATH - create [options] [SERVICE...] - down [options] [--rmi type] [--volumes] [--remove-orphans] - events [options] [SERVICE...] - exec [options] SERVICE COMMAND [ARGS...] - images [options] [SERVICE...] - kill [options] [SERVICE...] - logs [options] [SERVICE...] - ls [options] - pause [options] SERVICE... - port [options] SERVICE PRIVATE_PORT - ps [options] [SERVICE...] - pull [options] [SERVICE...] - push [options] [SERVICE...] - restart [options] [SERVICE...] - rm [options] [-f | -s] [SERVICE...] - run [options] [-p TARGET...] [-v VOLUME...] [-e KEY=VAL...] - [-l KEY=VAL...] SERVICE [COMMAND] [ARGS...] - scale [SERVICE=REPLICAS...] - start [options] [SERVICE...] - # todo: implement stats - # stats [options] [SERVICE] - stop [options] [SERVICE...] - top [options] [SERVICE...] - unpause [options] [SERVICE...] - up [options] [--scale SERVICE=NUM...] [--no-color] - [--quiet-pull] [SERVICE...] - version [options] - wait SERVICE [SERVICE...] [options] - watch [SERVICE...] - """ - - actions: list[str] = [ - 'attach', - 'build', - 'config', - 'cp', - 'create', - 'down', - 'events', - 'exec', - 'images', - 'kill', - 'ls', - 'logs', - 'pause', - 'port', - 'ps', - 'pull', - 'push', - 'restart', - 'rm', - 'run', - 'scale', - 'start', - 'stats', - 'stop', - 'top', - 'unpause', - 'up', - 'version', - 'wait', - 'watch', - ] - - def __init__( - self, - args: dict[str, str], - options_args: list[str] = [], - cmd_args: list[str] = [], - ): - """Initialize SugarDockerCompose instance.""" - super().__init__(args, options_args=options_args, cmd_args=cmd_args) - - # container commands - def _attach(self) -> None: - service_name = self.args.get('service', '') - service_name_list: list[str] = [service_name] if service_name else [] - self._call_compose_app('attach', services=service_name_list) - - def _build(self) -> None: - self._call_compose_app('build', services=self.service_names) - - def _config(self) -> None: - self._call_compose_app('config', services=self.service_names) - - def _cp(self) -> None: - self._call_compose_app('cp', services=[]) - - def _create(self) -> None: - self._call_compose_app('create', services=self.service_names) - - def _down(self) -> None: - if self.args.get('all') or self.args.get('services'): - SugarLogs.raise_error( - "The `down` sub-command doesn't accept `--all` " - 'neither `--services` parameters.', - SugarErrorType.SUGAR_INVALID_PARAMETER, - ) - - self._call_compose_app( - 'down', - '--remove-orphans', - services=[], - ) - - def _events(self) -> None: - # port is not complete supported - service_name = self.args.get('service', '') - if not service_name: - SugarLogs.raise_error( - '`exec` sub-command expected --service parameter.', - SugarErrorType.SUGAR_MISSING_PARAMETER, - ) - service_name_list = [service_name] if service_name else [] - self._call_compose_app('events', services=service_name_list) - - def _exec(self) -> None: - service_name = self.args.get('service', '') - if not service_name: - SugarLogs.raise_error( - '`exec` sub-command expected --service parameter.', - SugarErrorType.SUGAR_MISSING_PARAMETER, - ) - - service_name_list: list[str] = [service_name] if service_name else [] - self._call_compose_app('exec', services=service_name_list) - - def _images(self) -> None: - self._call_compose_app('images', services=self.service_names) - - def _kill(self) -> None: - self._call_compose_app('kill', services=self.service_names) - - def _logs(self) -> None: - self._call_compose_app('logs', services=self.service_names) - - def _ls(self) -> None: - self._call_compose_app('ls', services=[]) - - def _pause(self) -> None: - self._call_compose_app('pause', services=self.service_names) - - def _port(self) -> None: - # port is not complete supported - service_name = self.args.get('service', '') - if not service_name: - SugarLogs.raise_error( - '`exec` sub-command expected --service parameter.', - SugarErrorType.SUGAR_MISSING_PARAMETER, - ) - # TODO: check how private port could be passed - service_name_list: list[str] = [service_name] if service_name else [] - self._call_compose_app('port', services=service_name_list) - - def _ps(self) -> None: - self._call_compose_app('ps', services=self.service_names) - - def _pull(self) -> None: - self._call_compose_app('pull', services=self.service_names) - - def _push(self) -> None: - self._call_compose_app('push', services=self.service_names) - - def _restart(self) -> None: - self._call_compose_app('restart', services=self.service_names) - - def _rm(self) -> None: - self._call_compose_app('rm', services=self.service_names) - - def _run(self) -> None: - service_name = self.args.get('service', '') - if not service_name: - SugarLogs.raise_error( - '`run` sub-command expected --service parameter.', - SugarErrorType.SUGAR_MISSING_PARAMETER, - ) - service_name_list: list[str] = [service_name] if service_name else [] - self._call_compose_app('run', services=service_name_list) - - def _scale(self) -> None: - self._call_compose_app('ls', services=[]) - - def _start(self) -> None: - self._call_compose_app('start', services=self.service_names) - - def _stop(self) -> None: - self._call_compose_app('stop', services=self.service_names) - - def _top(self) -> None: - self._call_compose_app('top', services=self.service_names) - - def _unpause(self) -> None: - self._call_compose_app('unpause', services=self.service_names) - - def _up(self) -> None: - self._call_compose_app('up', services=self.service_names) - - def _wait(self) -> None: - self._call_compose_app('wait', services=self.service_names) - - def _watch(self) -> None: - self._call_compose_app('watch', services=self.service_names) + SugarLogs.print_info(f'Container Program Path: {self.backend_app}') + self._call_backend_app('version', services=[]) diff --git a/src/sugar/extensions/compose.py b/src/sugar/extensions/compose.py new file mode 100644 index 0000000..9637a16 --- /dev/null +++ b/src/sugar/extensions/compose.py @@ -0,0 +1,192 @@ +"""Sugar plugin for docker compose.""" + +from __future__ import annotations + +from sugar.extensions.base import SugarBase +from sugar.logs import SugarErrorType, SugarLogs + + +class SugarCompose(SugarBase): + """ + SugarCompose provides the docker compose commands. + + This are the commands that is currently provided: + + attach [options] SERVICE + build [options] [SERVICE...] + config [options] [SERVICE...] + cp: + - cp [options] SERVICE:SRC_PATH DEST_PATH|- + - cp [options] SRC_PATH|- SERVICE:DEST_PATH + create [options] [SERVICE...] + down [options] [--rmi type] [--volumes] [--remove-orphans] + events [options] [SERVICE...] + exec [options] SERVICE COMMAND [ARGS...] + images [options] [SERVICE...] + kill [options] [SERVICE...] + logs [options] [SERVICE...] + ls [options] + pause [options] SERVICE... + port [options] SERVICE PRIVATE_PORT + ps [options] [SERVICE...] + pull [options] [SERVICE...] + push [options] [SERVICE...] + restart [options] [SERVICE...] + rm [options] [-f | -s] [SERVICE...] + run [options] [-p TARGET...] [-v VOLUME...] [-e KEY=VAL...] + [-l KEY=VAL...] SERVICE [COMMAND] [ARGS...] + scale [SERVICE=REPLICAS...] + start [options] [SERVICE...] + # todo: implement stats + # stats [options] [SERVICE] + stop [options] [SERVICE...] + top [options] [SERVICE...] + unpause [options] [SERVICE...] + up [options] [--scale SERVICE=NUM...] [--no-color] + [--quiet-pull] [SERVICE...] + version [options] + wait SERVICE [SERVICE...] [options] + watch [SERVICE...] + """ + + def __init__( + self, + args: dict[str, str], + options_args: list[str] = [], + cmd_args: list[str] = [], + ): + """Initialize SugarCompose instance.""" + super().__init__(args, options_args=options_args, cmd_args=cmd_args) + + # container commands + def _cmd_attach(self) -> None: + service_name = self.args.get('service', '') + service_name_list: list[str] = [service_name] if service_name else [] + self._call_backend_app('attach', services=service_name_list) + + def _cmd_build(self) -> None: + self._call_backend_app('build', services=self.service_names) + + def _cmd_config(self) -> None: + self._call_backend_app('config', services=self.service_names) + + def _cmd_cp(self) -> None: + self._call_backend_app('cp', services=[]) + + def _cmd_create(self) -> None: + self._call_backend_app('create', services=self.service_names) + + def _cmd_down(self) -> None: + if self.args.get('all') or self.args.get('services'): + SugarLogs.raise_error( + "The `down` sub-command doesn't accept `--all` " + 'neither `--services` parameters.', + SugarErrorType.SUGAR_INVALID_PARAMETER, + ) + + self._call_backend_app( + 'down', + '--remove-orphans', + services=[], + ) + + def _cmd_events(self) -> None: + # port is not complete supported + service_name = self.args.get('service', '') + if not service_name: + SugarLogs.raise_error( + '`exec` sub-command expected --service parameter.', + SugarErrorType.SUGAR_MISSING_PARAMETER, + ) + service_name_list = [service_name] if service_name else [] + self._call_backend_app('events', services=service_name_list) + + def _cmd_exec(self) -> None: + service_name = self.args.get('service', '') + if not service_name: + SugarLogs.raise_error( + '`exec` sub-command expected --service parameter.', + SugarErrorType.SUGAR_MISSING_PARAMETER, + ) + + service_name_list: list[str] = [service_name] if service_name else [] + self._call_backend_app('exec', services=service_name_list) + + def _cmd_images(self) -> None: + self._call_backend_app('images', services=self.service_names) + + def _cmd_kill(self) -> None: + self._call_backend_app('kill', services=self.service_names) + + def _cmd_logs(self) -> None: + self._call_backend_app('logs', services=self.service_names) + + def _cmd_ls(self) -> None: + self._call_backend_app('ls', services=[]) + + def _cmd_pause(self) -> None: + self._call_backend_app('pause', services=self.service_names) + + def _cmd_port(self) -> None: + # port is not complete supported + service_name = self.args.get('service', '') + if not service_name: + SugarLogs.raise_error( + '`exec` sub-command expected --service parameter.', + SugarErrorType.SUGAR_MISSING_PARAMETER, + ) + # TODO: check how private port could be passed + service_name_list: list[str] = [service_name] if service_name else [] + self._call_backend_app('port', services=service_name_list) + + def _cmd_ps(self) -> None: + self._call_backend_app('ps', services=self.service_names) + + def _cmd_pull(self) -> None: + self._call_backend_app('pull', services=self.service_names) + + def _cmd_push(self) -> None: + self._call_backend_app('push', services=self.service_names) + + def _cmd_restart(self) -> None: + self._call_backend_app('restart', services=self.service_names) + + def _cmd_rm(self) -> None: + self._call_backend_app('rm', services=self.service_names) + + def _cmd_run(self) -> None: + service_name = self.args.get('service', '') + if not service_name: + SugarLogs.raise_error( + '`run` sub-command expected --service parameter.', + SugarErrorType.SUGAR_MISSING_PARAMETER, + ) + service_name_list: list[str] = [service_name] if service_name else [] + self._call_backend_app('run', services=service_name_list) + + def _cmd_scale(self) -> None: + self._call_backend_app('ls', services=[]) + + def _cmd_start(self) -> None: + self._call_backend_app('start', services=self.service_names) + + def _cmd_stop(self) -> None: + self._call_backend_app('stop', services=self.service_names) + + def _cmd_top(self) -> None: + self._call_backend_app('top', services=self.service_names) + + def _cmd_unpause(self) -> None: + self._call_backend_app('unpause', services=self.service_names) + + def _cmd_up(self) -> None: + self._call_backend_app('up', services=self.service_names) + + def _cmd_wait(self) -> None: + self._call_backend_app('wait', services=self.service_names) + + def _cmd_watch(self) -> None: + self._call_backend_app('watch', services=self.service_names) + + def _cmd_version(self) -> None: + self._call_backend_app('version', services=[]) diff --git a/src/sugar/plugins/ext.py b/src/sugar/extensions/compose_ext.py similarity index 54% rename from src/sugar/plugins/ext.py rename to src/sugar/extensions/compose_ext.py index 88355fd..15d7e40 100644 --- a/src/sugar/plugins/ext.py +++ b/src/sugar/extensions/compose_ext.py @@ -1,13 +1,13 @@ -"""SugarExt Plugin class for containers.""" +"""SugarComposeExt Plugin class for containers.""" from __future__ import annotations +from sugar.extensions.compose import SugarCompose from sugar.logs import SugarErrorType, SugarLogs -from sugar.plugins.base import SugarDockerCompose -class SugarExt(SugarDockerCompose): - """SugarExt provides special commands not available on docker-compose.""" +class SugarComposeExt(SugarCompose): + """SugarComposeExt provides extra commands on top of docker-compose.""" def __init__( self, @@ -15,34 +15,26 @@ def __init__( options_args: list[str] = [], cmd_args: list[str] = [], ) -> None: - """Initialize the SugarExt class.""" - self.actions += [ - 'get-ip', - 'restart', - 'start', - 'stop', - 'wait', - ] - + """Initialize the SugarComposeExt class.""" super().__init__(args, options_args=options_args, cmd_args=cmd_args) - def _get_ip(self) -> None: + def _cmd_get_ip(self) -> None: SugarLogs.raise_error( '`get-ip` mot implemented yet.', SugarErrorType.SUGAR_ACTION_NOT_IMPLEMENTED, ) - def _restart(self) -> None: + def _cmd_restart(self) -> None: options = self.options_args self.options_args = [] - self._stop() + self._cmd_stop() self.options_args = options - self._start() + self._cmd_start() - def _start(self) -> None: - self._up() + def _cmd_start(self) -> None: + self._cmd_up() - def _wait(self) -> None: + def _cmd_wait(self) -> None: SugarLogs.raise_error( '`wait` not implemented yet.', SugarErrorType.SUGAR_ACTION_NOT_IMPLEMENTED, diff --git a/src/sugar/plugins/stats.py b/src/sugar/extensions/stats.py similarity index 94% rename from src/sugar/plugins/stats.py rename to src/sugar/extensions/stats.py index d404c9a..d8275f0 100644 --- a/src/sugar/plugins/stats.py +++ b/src/sugar/extensions/stats.py @@ -18,9 +18,9 @@ from textual.widgets import Header from sugar.console import get_terminal_size +from sugar.extensions.compose import SugarCompose from sugar.inspect import get_container_name, get_container_stats from sugar.logs import SugarErrorType, SugarLogs -from sugar.plugins.base import SugarDockerCompose CHART_WINDOW_DURATION = 60 CHART_TIME_INTERVAL = 1 @@ -260,28 +260,15 @@ def compose(self) -> ComposeResult: yield StatsPlotWidget(self.container_names) -class SugarStats(SugarDockerCompose): +class SugarStats(SugarCompose): """SugarStats provides special commands not available on docker-compose.""" - def __init__( - self, - args: dict[str, str], - options_args: list[str] = [], - cmd_args: list[str] = [], - ) -> None: - """Initialize the SugarExt class.""" - self.actions += [ - 'plot', - ] - - super().__init__(args, options_args=options_args, cmd_args=cmd_args) - - def _plot(self) -> None: + def _cmd_plot(self) -> None: """Call the plot command.""" _out = io.StringIO() _err = io.StringIO() - self._call_compose_app_core( + self._call_backend_app_core( 'ps', services=self.service_names, options_args=['-q'], diff --git a/test.yaml b/test.yaml index 0070883..aa13088 100644 --- a/test.yaml +++ b/test.yaml @@ -1,4 +1,4 @@ - name: group2 project-name: null #optional - compose-path: containers/tests2/group2/compose.yaml + config-path: containers/tests2/group2/compose.yaml env-file: .env diff --git a/tests/containers/.services.sugar.yaml b/tests/containers/.services.sugar.yaml index e6aafff..a2c6b99 100644 --- a/tests/containers/.services.sugar.yaml +++ b/tests/containers/.services.sugar.yaml @@ -1,10 +1,10 @@ version: 1.0 -compose-app: docker-compose +backend: docker-compose defaults: project-name: sugar-{{ env.SUGAR_PROJECT_NAME }} services: project-name: project1 # optional - compose-path: tests/containers/group1/compose.yaml + config-path: tests/containers/group1/compose.yaml default: service1-1,service1-3 available: - name: service1-1 diff --git a/tests/test_containers_sugar.py b/tests/test_containers_sugar.py index 94da870..b810edd 100644 --- a/tests/test_containers_sugar.py +++ b/tests/test_containers_sugar.py @@ -9,7 +9,7 @@ CONFIG_PATH = Path(__file__).parent.parent / '.sugar.yaml' DEFAULT_ARGS = { - 'compose-app': 'docker compose', + 'backend': 'docker compose', 'action': '', 'config_file': '', 'service_group': '', @@ -26,9 +26,17 @@ @pytest.mark.parametrize( 'args', [ - {'version': True}, {'help': True}, - {'action': 'config', 'service_group': 'group1'}, + {'plugin': 'compose', 'action': 'version', 'version': True}, + {'plugin': 'compose', 'help': True}, + {'plugin': 'compose', 'action': 'config', 'service_group': 'group1'}, + {'plugin': 'compose-ext', 'action': 'version', 'version': True}, + {'plugin': 'compose-ext', 'help': True}, + { + 'plugin': 'compose-ext', + 'action': 'config', + 'service_group': 'group1', + }, ], ) def test_success(args):