aws-annoying 0.4.0__py3-none-any.whl → 0.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. aws_annoying/cli/app.py +81 -0
  2. aws_annoying/cli/ecs/__init__.py +3 -0
  3. aws_annoying/cli/ecs/_app.py +9 -0
  4. aws_annoying/cli/{ecs_task_definition_lifecycle.py → ecs/task_definition_lifecycle.py} +18 -13
  5. aws_annoying/cli/ecs/wait_for_deployment.py +94 -0
  6. aws_annoying/cli/load_variables.py +22 -22
  7. aws_annoying/cli/logging_handler.py +52 -0
  8. aws_annoying/cli/main.py +1 -1
  9. aws_annoying/cli/mfa/configure.py +21 -12
  10. aws_annoying/cli/session_manager/_common.py +1 -2
  11. aws_annoying/cli/session_manager/install.py +10 -7
  12. aws_annoying/cli/session_manager/port_forward.py +41 -38
  13. aws_annoying/cli/session_manager/start.py +48 -2
  14. aws_annoying/cli/session_manager/stop.py +9 -7
  15. aws_annoying/ecs/__init__.py +17 -0
  16. aws_annoying/ecs/common.py +8 -0
  17. aws_annoying/ecs/deployment_waiter.py +274 -0
  18. aws_annoying/ecs/errors.py +14 -0
  19. aws_annoying/{mfa.py → mfa_config.py} +7 -2
  20. aws_annoying/session_manager/__init__.py +8 -1
  21. aws_annoying/session_manager/session_manager.py +26 -39
  22. aws_annoying/session_manager/shortcuts.py +76 -0
  23. aws_annoying/utils/ec2.py +36 -0
  24. aws_annoying/utils/platform.py +11 -0
  25. aws_annoying/utils/timeout.py +88 -0
  26. aws_annoying/{variables.py → variable_loader.py} +11 -16
  27. {aws_annoying-0.4.0.dist-info → aws_annoying-0.6.0.dist-info}/METADATA +47 -2
  28. aws_annoying-0.6.0.dist-info/RECORD +41 -0
  29. aws_annoying-0.4.0.dist-info/RECORD +0 -30
  30. {aws_annoying-0.4.0.dist-info → aws_annoying-0.6.0.dist-info}/WHEEL +0 -0
  31. {aws_annoying-0.4.0.dist-info → aws_annoying-0.6.0.dist-info}/entry_points.txt +0 -0
  32. {aws_annoying-0.4.0.dist-info → aws_annoying-0.6.0.dist-info}/licenses/LICENSE +0 -0
aws_annoying/cli/app.py CHANGED
@@ -1,6 +1,13 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import importlib.metadata
4
+ import logging
5
+ import logging.config
6
+ from typing import Optional
7
+
3
8
  import typer
9
+ from rich import print # noqa: A004
10
+ from rich.console import Console
4
11
 
5
12
  app = typer.Typer(
6
13
  pretty_exceptions_short=True,
@@ -8,3 +15,77 @@ app = typer.Typer(
8
15
  rich_markup_mode="rich",
9
16
  no_args_is_help=True,
10
17
  )
18
+
19
+
20
+ def show_version(value: Optional[bool]) -> None:
21
+ """Show the version of the application."""
22
+ if not value:
23
+ return
24
+
25
+ print(importlib.metadata.version("aws-annoying"))
26
+ raise typer.Exit(0)
27
+
28
+
29
+ @app.callback()
30
+ def main( # noqa: D103
31
+ ctx: typer.Context,
32
+ *,
33
+ version: Optional[bool] = typer.Option( # noqa: ARG001
34
+ None,
35
+ "--version",
36
+ is_eager=True,
37
+ callback=show_version,
38
+ help="Show the version and exit.",
39
+ ),
40
+ quiet: bool = typer.Option(
41
+ False, # noqa: FBT003
42
+ help="Disable outputs.",
43
+ ),
44
+ verbose: bool = typer.Option(
45
+ False, # noqa: FBT003
46
+ help="Enable verbose outputs.",
47
+ ),
48
+ dry_run: bool = typer.Option(
49
+ False, # noqa: FBT003
50
+ help="Enable dry-run mode. If enabled, certain commands will avoid making changes.",
51
+ ),
52
+ ) -> None:
53
+ log_level = logging.DEBUG if verbose else logging.INFO
54
+ console = Console(soft_wrap=True, emoji=False)
55
+ logging_config: logging.config._DictConfigArgs = {
56
+ "version": 1,
57
+ "disable_existing_loggers": False,
58
+ "formatters": {
59
+ "rich": {
60
+ "format": "%(message)s",
61
+ "datefmt": "[%X]",
62
+ },
63
+ },
64
+ "handlers": {
65
+ "null": {
66
+ "class": "logging.NullHandler",
67
+ },
68
+ "rich": {
69
+ "class": "aws_annoying.cli.logging_handler.RichLogHandler",
70
+ "formatter": "rich",
71
+ "console": console,
72
+ },
73
+ },
74
+ "root": {
75
+ "handlers": ["null"],
76
+ },
77
+ "loggers": {
78
+ "aws_annoying": {
79
+ "level": log_level,
80
+ "handlers": ["rich"],
81
+ "propagate": True,
82
+ },
83
+ },
84
+ }
85
+ if quiet:
86
+ logging_config["loggers"]["aws_annoying"]["level"] = logging.CRITICAL
87
+
88
+ logging.config.dictConfig(logging_config)
89
+
90
+ # Global flags
91
+ ctx.meta["dry_run"] = dry_run
@@ -0,0 +1,3 @@
1
+ from . import task_definition_lifecycle, wait_for_deployment
2
+
3
+ __all__ = ("task_definition_lifecycle", "wait_for_deployment")
@@ -0,0 +1,9 @@
1
+ import typer
2
+
3
+ from aws_annoying.cli.app import app
4
+
5
+ ecs_app = typer.Typer(
6
+ no_args_is_help=True,
7
+ help="ECS (Elastic Container Service) utility commands.",
8
+ )
9
+ app.add_typer(ecs_app, name="ecs")
@@ -1,21 +1,25 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import logging
3
4
  from typing import TYPE_CHECKING
4
5
 
5
6
  import boto3
6
7
  import typer
7
- from rich import print # noqa: A004
8
8
 
9
- from .app import app
9
+ from ._app import ecs_app
10
10
 
11
11
  if TYPE_CHECKING:
12
12
  from collections.abc import Iterator
13
13
 
14
+
15
+ logger = logging.getLogger(__name__)
16
+
14
17
  _DELETE_CHUNK_SIZE = 10
15
18
 
16
19
 
17
- @app.command()
18
- def ecs_task_definition_lifecycle(
20
+ @ecs_app.command()
21
+ def task_definition_lifecycle(
22
+ ctx: typer.Context,
19
23
  *,
20
24
  family: str = typer.Option(
21
25
  ...,
@@ -33,14 +37,11 @@ def ecs_task_definition_lifecycle(
33
37
  False, # noqa: FBT003
34
38
  help="Delete the task definition after deregistering it.",
35
39
  ),
36
- dry_run: bool = typer.Option(
37
- False, # noqa: FBT003
38
- help="Do not perform any changes, only show what would be done.",
39
- ),
40
40
  ) -> None:
41
41
  """Execute ECS task definition lifecycle."""
42
+ dry_run = ctx.meta["dry_run"]
42
43
  if dry_run:
43
- print("⚠️ Dry run mode enabled. Will not perform any actual changes.")
44
+ logger.info("Dry run mode enabled. Will not perform any actual changes.")
44
45
 
45
46
  ecs = boto3.client("ecs")
46
47
 
@@ -59,23 +60,27 @@ def ecs_task_definition_lifecycle(
59
60
 
60
61
  # Keep the latest N task definitions
61
62
  expired_taskdef_arns = task_definition_arns[:-keep_latest]
62
- print(f"⚠️ Deregistering {len(expired_taskdef_arns)} task definitions...")
63
+ logger.warning("Deregistering %d task definitions...", len(expired_taskdef_arns))
63
64
  for arn in expired_taskdef_arns:
64
65
  if not dry_run:
65
66
  ecs.deregister_task_definition(taskDefinition=arn)
66
67
 
67
68
  # ARN like: "arn:aws:ecs:<region>:<account-id>:task-definition/<family>:<revision>"
68
69
  _, family_revision = arn.split(":task-definition/")
69
- print(f"Deregistered task definition [yellow]{family_revision!r}[/yellow]")
70
+ logger.warning("Deregistered task definition [yellow]%r[/yellow]", family_revision)
70
71
 
71
72
  if delete and expired_taskdef_arns:
72
73
  # Delete the expired task definitions in chunks due to API limitation
73
- print(f"⚠️ Deleting {len(expired_taskdef_arns)} task definitions in chunks of size {_DELETE_CHUNK_SIZE}...")
74
+ logger.warning(
75
+ "Deleting %d task definitions in chunks of size %d...",
76
+ len(expired_taskdef_arns),
77
+ _DELETE_CHUNK_SIZE,
78
+ )
74
79
  for idx, chunk in enumerate(_chunker(expired_taskdef_arns, _DELETE_CHUNK_SIZE)):
75
80
  if not dry_run:
76
81
  ecs.delete_task_definitions(taskDefinitions=chunk)
77
82
 
78
- print(f"Deleted {len(chunk)} task definitions in {idx}-th batch.")
83
+ logger.warning("Deleted %d task definitions in %d-th batch.", len(chunk), idx)
79
84
 
80
85
 
81
86
  def _chunker(sequence: list, size: int) -> Iterator[list]:
@@ -0,0 +1,94 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from datetime import datetime, timezone
5
+ from typing import Optional
6
+
7
+ import typer
8
+
9
+ from aws_annoying.ecs import DeploymentFailedError, ECSDeploymentWaiter, ECSServiceRef
10
+ from aws_annoying.utils.timeout import OperationTimeoutError, Timeout
11
+
12
+ from ._app import ecs_app
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ @ecs_app.command()
18
+ def wait_for_deployment( # noqa: PLR0913
19
+ *,
20
+ cluster: str = typer.Option(
21
+ ...,
22
+ help="The name of the ECS cluster.",
23
+ show_default=False,
24
+ ),
25
+ service: str = typer.Option(
26
+ ...,
27
+ help="The name of the ECS service.",
28
+ show_default=False,
29
+ ),
30
+ expected_task_definition: Optional[str] = typer.Option(
31
+ None,
32
+ help=(
33
+ "The service's task definition expected after deployment."
34
+ " If provided, it will be used to assert the service's task definition after deployment finished or timed out." # noqa: E501
35
+ ),
36
+ show_default=False,
37
+ ),
38
+ polling_interval: int = typer.Option(
39
+ 5,
40
+ help="The interval between any polling attempts, in seconds.",
41
+ min=1,
42
+ ),
43
+ timeout_seconds: Optional[int] = typer.Option(
44
+ None,
45
+ help=(
46
+ "The maximum time to wait for the deployment to complete, in seconds."
47
+ " If not provided, it will wait indefinitely."
48
+ ),
49
+ min=1,
50
+ ),
51
+ wait_for_start: bool = typer.Option(
52
+ True, # noqa: FBT003
53
+ help=(
54
+ "Whether to wait for the deployment to start."
55
+ " Because there could be no deployment right after the deploy,"
56
+ " this option will wait for a new deployment to start if no running deployment is found."
57
+ ),
58
+ ),
59
+ wait_for_stability: bool = typer.Option(
60
+ False, # noqa: FBT003
61
+ help="Whether to wait for the service to be stable after the deployment.",
62
+ ),
63
+ ) -> None:
64
+ """Wait for ECS deployment to complete."""
65
+ start = datetime.now(tz=timezone.utc)
66
+ waiter = ECSDeploymentWaiter(ECSServiceRef(cluster=cluster, service=service))
67
+ try:
68
+ with Timeout(timeout_seconds):
69
+ waiter.wait(
70
+ wait_for_start=wait_for_start,
71
+ polling_interval=polling_interval,
72
+ wait_for_stability=wait_for_stability,
73
+ expected_task_definition=expected_task_definition,
74
+ )
75
+ except OperationTimeoutError:
76
+ logger.error( # noqa: TRY400
77
+ "Timeout reached after %s seconds. The deployment may not have finished.",
78
+ timeout_seconds,
79
+ )
80
+ raise typer.Exit(1) from None
81
+ except DeploymentFailedError as err:
82
+ elapsed = datetime.now(tz=timezone.utc) - start
83
+ logger.error( # noqa: TRY400
84
+ "Deployment failed in [bold]%.2f[/bold] seconds with error: %s",
85
+ elapsed.total_seconds(),
86
+ err,
87
+ )
88
+ raise typer.Exit(1) from None
89
+ else:
90
+ elapsed = datetime.now(tz=timezone.utc) - start
91
+ logger.info(
92
+ "Deployment completed in [bold]%.2f[/bold] seconds.",
93
+ elapsed.total_seconds(),
94
+ )
@@ -1,18 +1,22 @@
1
1
  # flake8: noqa: B008
2
2
  from __future__ import annotations
3
3
 
4
+ import logging
4
5
  import os
5
6
  import subprocess
7
+ from io import StringIO
6
8
  from typing import NoReturn, Optional
7
9
 
8
10
  import typer
9
11
  from rich.console import Console
10
12
  from rich.table import Table
11
13
 
12
- from aws_annoying.variables import VariableLoader
14
+ from aws_annoying.variable_loader import VariableLoader
13
15
 
14
16
  from .app import app
15
17
 
18
+ logger = logging.getLogger(__name__)
19
+
16
20
 
17
21
  @app.command(
18
22
  context_settings={
@@ -21,9 +25,9 @@ from .app import app
21
25
  "ignore_unknown_options": True,
22
26
  },
23
27
  )
24
- def load_variables( # noqa: PLR0913
25
- *,
28
+ def load_variables(
26
29
  ctx: typer.Context,
30
+ *,
27
31
  arns: list[str] = typer.Option(
28
32
  [],
29
33
  metavar="ARN",
@@ -42,14 +46,6 @@ def load_variables( # noqa: PLR0913
42
46
  False, # noqa: FBT003
43
47
  help="Overwrite the existing environment variables with the same name.",
44
48
  ),
45
- quiet: bool = typer.Option(
46
- False, # noqa: FBT003
47
- help="Suppress all outputs from this command.",
48
- ),
49
- dry_run: bool = typer.Option(
50
- False, # noqa: FBT003
51
- help="Print the progress only. Neither load variables nor run the command.",
52
- ),
53
49
  replace: bool = typer.Option(
54
50
  True, # noqa: FBT003
55
51
  help=(
@@ -82,21 +78,21 @@ def load_variables( # noqa: PLR0913
82
78
  The variables are loaded in the order of option provided, overwriting the variables with the same name in the order of the ARNs.
83
79
  Existing environment variables are preserved by default, unless `--overwrite-env` is provided.
84
80
  """ # noqa: E501
85
- console = Console(quiet=quiet, emoji=False)
81
+ dry_run = ctx.meta["dry_run"]
86
82
 
87
83
  command = ctx.args
88
84
  if not command:
89
- console.print("⚠️ No command provided. Exiting...")
85
+ logger.warning("No command provided. Exiting...")
90
86
  raise typer.Exit(0)
91
87
 
92
88
  # Mapping of the ARNs by index (index used for ordering)
93
89
  map_arns_by_index = {str(idx): arn for idx, arn in enumerate(arns)}
94
90
  if env_prefix:
95
- console.print(f"🔍 Loading ARNs from environment variables with prefix: {env_prefix!r}")
91
+ logger.info("Loading ARNs from environment variables with prefix: %r", env_prefix)
96
92
  arns_env = {
97
93
  key.removeprefix(env_prefix): value for key, value in os.environ.items() if key.startswith(env_prefix)
98
94
  }
99
- console.print(f"🔍 Found {len(arns_env)} sources from environment variables.")
95
+ logger.info("Found %d sources from environment variables.", len(arns_env))
100
96
  map_arns_by_index = arns_env | map_arns_by_index
101
97
 
102
98
  # Briefly show the ARNs
@@ -104,21 +100,25 @@ def load_variables( # noqa: PLR0913
104
100
  for idx, arn in sorted(map_arns_by_index.items()):
105
101
  table.add_row(idx, arn)
106
102
 
107
- console.print(table)
103
+ # Workaround: The logger cannot directly handle the rich table output.
104
+ with StringIO() as file:
105
+ Console(file=file, emoji=False).print(table)
106
+ table_str = file.getvalue().rstrip()
107
+ logger.info("Summary:\n%s", table_str)
108
108
 
109
109
  # Retrieve the variables
110
- loader = VariableLoader(dry_run=dry_run)
111
- console.print("🔍 Retrieving variables from AWS resources...")
110
+ loader = VariableLoader()
111
+ logger.info("Retrieving variables from AWS resources...")
112
112
  if dry_run:
113
- console.print("⚠️ Dry run mode enabled. Variables won't be loaded from AWS.")
113
+ logger.warning("Dry run mode enabled. Variables won't be loaded from AWS.")
114
114
 
115
115
  try:
116
116
  variables, load_stats = loader.load(map_arns_by_index)
117
117
  except Exception as exc: # noqa: BLE001
118
- console.print(f"Failed to load the variables: {exc!s}")
118
+ logger.error("Failed to load the variables: %s", exc) # noqa: TRY400
119
119
  raise typer.Exit(1) from None
120
120
 
121
- console.print(f"Retrieved {load_stats['secrets']} secrets and {load_stats['parameters']} parameters.")
121
+ logger.info("Retrieved %d secrets and %d parameters.", load_stats["secrets"], load_stats["parameters"])
122
122
 
123
123
  # Prepare the environment variables
124
124
  env = os.environ.copy()
@@ -130,7 +130,7 @@ def load_variables( # noqa: PLR0913
130
130
  env.setdefault(key, str(value))
131
131
 
132
132
  # Run the command with the variables injected as environment variables, replacing current process
133
- console.print(f"🚀 Running the command: [bold orchid]{' '.join(command)}[/bold orchid]")
133
+ logger.info("Running the command: [bold orchid]%s[/bold orchid]", " ".join(command))
134
134
  if replace: # pragma: no cover (not coverable)
135
135
  os.execvpe(command[0], command, env=env) # noqa: S606
136
136
  # The above line should never return
@@ -0,0 +1,52 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import logging.config
5
+ from typing import TYPE_CHECKING, Any, Final
6
+
7
+ from typing_extensions import override
8
+
9
+ if TYPE_CHECKING:
10
+ from rich.console import Console
11
+
12
+
13
+ class RichLogHandler(logging.Handler):
14
+ """Custom logging handler to use Rich Console."""
15
+
16
+ _level_emojis: Final[dict[str, str]] = {
17
+ "DEBUG": "🔍",
18
+ "INFO": "ℹ️", # noqa: RUF001
19
+ "WARNING": "⚠️",
20
+ "ERROR": "❗",
21
+ "CRITICAL": "🚨",
22
+ }
23
+
24
+ def __init__(self, console: Console, *args: Any, **kwargs: Any) -> None:
25
+ """Initialize the log handler.
26
+
27
+ Args:
28
+ console: Rich console instance.
29
+ *args: Additional arguments for the logging handler.
30
+ **kwargs: Additional keyword arguments for the logging handler.
31
+ """
32
+ super().__init__(*args, **kwargs)
33
+ self.console = console
34
+
35
+ @override
36
+ def emit(self, record: logging.LogRecord) -> None:
37
+ msg = self.format(record)
38
+ self.console.print(msg)
39
+
40
+ @override
41
+ def format(self, record: logging.LogRecord) -> str:
42
+ """Format the log record.
43
+
44
+ Args:
45
+ record: The log record to format.
46
+
47
+ Returns:
48
+ The formatted log message.
49
+ """
50
+ msg = super().format(record)
51
+ emoji = self._level_emojis.get(record.levelname)
52
+ return f"{emoji} {msg}" if emoji else msg
aws_annoying/cli/main.py CHANGED
@@ -1,7 +1,7 @@
1
1
  # flake8: noqa: F401
2
2
  from __future__ import annotations
3
3
 
4
- import aws_annoying.cli.ecs_task_definition_lifecycle
4
+ import aws_annoying.cli.ecs
5
5
  import aws_annoying.cli.load_variables
6
6
  import aws_annoying.cli.mfa
7
7
  import aws_annoying.cli.session_manager
@@ -1,18 +1,18 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import logging
3
4
  from pathlib import Path # noqa: TC003
4
5
  from typing import Optional
5
6
 
6
7
  import boto3
7
8
  import typer
8
- from rich import print # noqa: A004
9
9
  from rich.prompt import Prompt
10
10
 
11
- from aws_annoying.mfa import MfaConfig, update_credentials
11
+ from aws_annoying.mfa_config import MfaConfig, update_credentials
12
12
 
13
13
  from ._app import mfa_app
14
14
 
15
- _CONFIG_INI_SECTION = "aws-annoying:mfa"
15
+ logger = logging.getLogger(__name__)
16
16
 
17
17
 
18
18
  @mfa_app.command()
@@ -44,6 +44,10 @@ def configure( # noqa: PLR0913
44
44
  "~/.aws/config",
45
45
  help="The path to the AWS config file. Used to persist the MFA configuration.",
46
46
  ),
47
+ aws_config_section: str = typer.Option(
48
+ "aws-annoying:mfa",
49
+ help="The section in the AWS config file to persist the MFA configuration.",
50
+ ),
47
51
  persist: bool = typer.Option(
48
52
  True, # noqa: FBT003
49
53
  help="Persist the MFA configuration.",
@@ -55,9 +59,9 @@ def configure( # noqa: PLR0913
55
59
  aws_config = aws_config.expanduser()
56
60
 
57
61
  # Load configuration
58
- mfa_config, exists = MfaConfig.from_ini_file(aws_config, _CONFIG_INI_SECTION)
62
+ mfa_config, exists = MfaConfig.from_ini_file(aws_config, aws_config_section)
59
63
  if exists:
60
- print(f"⚙️ Loaded MFA configuration from AWS config ({aws_config}).")
64
+ logger.info("Loaded MFA configuration from AWS config (%s).", aws_config)
61
65
 
62
66
  mfa_profile = (
63
67
  mfa_profile
@@ -83,7 +87,7 @@ def configure( # noqa: PLR0913
83
87
  )
84
88
 
85
89
  # Get credentials
86
- print(f"💬 Retrieving MFA credentials using profile [bold]{mfa_source_profile}[/bold]")
90
+ logger.info("Retrieving MFA credentials using profile [bold]%s[/bold]", mfa_source_profile)
87
91
  session = boto3.session.Session(profile_name=mfa_source_profile)
88
92
  sts = session.client("sts")
89
93
  response = sts.get_session_token(
@@ -93,7 +97,11 @@ def configure( # noqa: PLR0913
93
97
  credentials = response["Credentials"]
94
98
 
95
99
  # Update MFA profile in AWS credentials
96
- print(f"✅ Updating MFA profile ([bold]{mfa_profile}[/bold]) to AWS credentials ({aws_credentials})")
100
+ logger.warning(
101
+ "Updating MFA profile ([bold]%s[/bold]) to AWS credentials ([bold]%s[/bold])",
102
+ mfa_profile,
103
+ aws_credentials,
104
+ )
97
105
  update_credentials(
98
106
  aws_credentials,
99
107
  mfa_profile, # type: ignore[arg-type]
@@ -104,13 +112,14 @@ def configure( # noqa: PLR0913
104
112
 
105
113
  # Persist MFA configuration
106
114
  if persist:
107
- print(
108
- f"Persisting MFA configuration in AWS config ({aws_config}),"
109
- f" in [bold]{_CONFIG_INI_SECTION}[/bold] section.",
115
+ logger.info(
116
+ "Persisting MFA configuration in AWS config (%s), in [bold]%s[/bold] section.",
117
+ aws_config,
118
+ aws_config_section,
110
119
  )
111
120
  mfa_config.mfa_profile = mfa_profile
112
121
  mfa_config.mfa_source_profile = mfa_source_profile
113
122
  mfa_config.mfa_serial_number = mfa_serial_number
114
- mfa_config.save_ini_file(aws_config, _CONFIG_INI_SECTION)
123
+ mfa_config.save_ini_file(aws_config, aws_config_section)
115
124
  else:
116
- print("⚠️ MFA configuration not persisted.")
125
+ logger.warning("MFA configuration not persisted.")
@@ -1,4 +1,3 @@
1
- # TODO(lasuillard): Using this file until split CLI from library codebase
2
1
  from __future__ import annotations
3
2
 
4
3
  from typing import Any
@@ -15,7 +14,7 @@ class SessionManager(_SessionManager):
15
14
  if self._confirm:
16
15
  return
17
16
 
18
- confirm = Confirm.ask(f"⚠️ Will run the following command: [bold red]{' '.join(command)}[/bold red]. Proceed?")
17
+ confirm = Confirm.ask(f"Will run the following command: [bold red]{' '.join(command)}[/bold red]. Proceed?")
19
18
  if not confirm:
20
19
  raise typer.Abort
21
20
 
@@ -1,13 +1,16 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import logging
4
+
3
5
  import typer
4
- from rich import print # noqa: A004
5
6
 
6
7
  from aws_annoying.utils.downloader import TQDMDownloader
7
8
 
8
9
  from ._app import session_manager_app
9
10
  from ._common import SessionManager
10
11
 
12
+ logger = logging.getLogger(__name__)
13
+
11
14
 
12
15
  # https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html
13
16
  @session_manager_app.command()
@@ -18,22 +21,22 @@ def install(
18
21
  ),
19
22
  ) -> None:
20
23
  """Install AWS Session Manager plugin."""
21
- session_manager = SessionManager(downloader=TQDMDownloader())
24
+ session_manager = SessionManager()
22
25
 
23
26
  # Check session-manager-plugin already installed
24
27
  is_installed, binary_path, version = session_manager.verify_installation()
25
28
  if is_installed:
26
- print(f"Session Manager plugin is already installed at {binary_path} (version: {version})")
29
+ logger.info("Session Manager plugin is already installed at %s (version: %s)", binary_path, version)
27
30
  return
28
31
 
29
32
  # Install session-manager-plugin
30
- print("⬇️ Installing AWS Session Manager plugin. You could be prompted for admin privileges request.")
31
- session_manager.install(confirm=yes)
33
+ logger.warning("Installing AWS Session Manager plugin. You could be prompted for admin privileges request.")
34
+ session_manager.install(confirm=yes, downloader=TQDMDownloader())
32
35
 
33
36
  # Verify installation
34
37
  is_installed, binary_path, version = session_manager.verify_installation()
35
38
  if not is_installed:
36
- print("Installation failed. Session Manager plugin not found.")
39
+ logger.error("Installation failed. Session Manager plugin not found.")
37
40
  raise typer.Exit(1)
38
41
 
39
- print(f"Session Manager plugin successfully installed at {binary_path} (version: {version})")
42
+ logger.info("Session Manager plugin successfully installed at %s (version: %s)", binary_path, version)