cloudsnake 0.6.0__tar.gz → 0.10.0__tar.gz

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 (38) hide show
  1. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/PKG-INFO +66 -10
  2. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/README.md +59 -6
  3. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/pyproject.toml +8 -5
  4. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/cli/cli.py +15 -13
  5. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/cli/dto.py +13 -0
  6. cloudsnake-0.10.0/src/cloudsnake/cli/logs.py +74 -0
  7. cloudsnake-0.10.0/src/cloudsnake/cli/ssm.py +135 -0
  8. cloudsnake-0.10.0/src/cloudsnake/cli/sso.py +155 -0
  9. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/helpers.py +77 -1
  10. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/logger.py +2 -1
  11. cloudsnake-0.10.0/src/cloudsnake/models/selector.py +9 -0
  12. cloudsnake-0.10.0/src/cloudsnake/sdk/cloudwatch.py +119 -0
  13. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/sdk/session.py +1 -1
  14. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/sdk/ssm_parameters.py +6 -4
  15. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/sdk/ssm_session.py +4 -4
  16. cloudsnake-0.10.0/src/cloudsnake/sdk/sso.py +71 -0
  17. cloudsnake-0.10.0/src/cloudsnake/sdk/sso_oidc.py +65 -0
  18. cloudsnake-0.10.0/src/cloudsnake/styles/tui.tcss +58 -0
  19. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/tui.py +1 -0
  20. cloudsnake-0.10.0/src/cloudsnake/tui_v2.py +96 -0
  21. cloudsnake-0.10.0/src/cloudsnake/utils.py +63 -0
  22. cloudsnake-0.10.0/tests/test_cli.py +12 -0
  23. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/tests/test_helpers.py +0 -7
  24. cloudsnake-0.6.0/src/cloudsnake/cli/ssm.py +0 -107
  25. cloudsnake-0.6.0/tests/test_cli.py +0 -12
  26. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/CHANGELOG.md +0 -0
  27. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/LICENSE +0 -0
  28. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/__init__.py +0 -0
  29. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/__main__.py +0 -0
  30. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/cli/rds.py +0 -0
  31. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/sdk/aws.py +0 -0
  32. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/sdk/ec2.py +0 -0
  33. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/sdk/rds_session.py +0 -0
  34. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/tests/README.md +0 -0
  35. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/tests/__init__.py +0 -0
  36. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/tests/conftest.py +0 -0
  37. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/tests/test_logger.py +0 -0
  38. {cloudsnake-0.6.0 → cloudsnake-0.10.0}/tests/test_rds.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudsnake
3
- Version: 0.6.0
3
+ Version: 0.10.0
4
4
  Summary: Some AWS CLI commands with a beautiful TUI
5
5
  License: GPL-3.0
6
6
  License-File: LICENSE
@@ -22,15 +22,18 @@ Classifier: Programming Language :: Python :: 3.10
22
22
  Classifier: Programming Language :: Python :: 3.11
23
23
  Classifier: Programming Language :: Python :: Implementation :: CPython
24
24
  Classifier: Programming Language :: Python :: Implementation :: PyPy
25
- Requires-Dist: boto3 (>=1.41.5,<2.0.0)
25
+ Requires-Dist: boto3 (>=1.42.10,<2.0.0)
26
+ Requires-Dist: boto3-stubs (>=1.42.8,<1.43.0)
26
27
  Requires-Dist: click (<8.4.0)
28
+ Requires-Dist: configparser (>=7.2.0,<8.0.0)
27
29
  Requires-Dist: dacite (>=1.8.1,<2.0.0)
28
30
  Requires-Dist: jmespath (>=1.0.1,<2.0.0)
29
31
  Requires-Dist: moto (>=5.0.9,<6.0.0)
30
- Requires-Dist: requests (>=2.32.3,<3.0.0)
32
+ Requires-Dist: requests (>=2.32.5,<3.0.0)
31
33
  Requires-Dist: rich (>=14.2.0,<15.0.0)
32
34
  Requires-Dist: simple-term-menu (>=1.6.4,<2.0.0)
33
- Requires-Dist: typer (>=0.20.0,<0.21.0)
35
+ Requires-Dist: textual (>=7.2.0,<8.0.0)
36
+ Requires-Dist: typer (>=0.20,<0.22)
34
37
  Requires-Dist: typing-extensions (>=4.12.0,<5.0.0)
35
38
  Project-URL: Documentation, https://github.com/containerscrew/cloudsnake#readme
36
39
  Project-URL: Repository, https://github.com/containerscrew/cloudsnake
@@ -38,7 +41,7 @@ Description-Content-Type: text/markdown
38
41
 
39
42
  <p align="center" >
40
43
  <h3 align="center">cloudsnake 🐍</h3>
41
- <p align="center">Wrapping some useful AWS cli commands to operate AWS like EC2 SSM instance connection or RDS connection using IAM authentication</p>
44
+ <p align="center">Wrapping some useful AWS cli commands to operate some services like EC2, SSO and more</p>
42
45
  </p>
43
46
 
44
47
  ---
@@ -55,18 +58,28 @@ Description-Content-Type: text/markdown
55
58
  <img alt="PyPiP version" src="https://img.shields.io/pypi/v/cloudsnake">
56
59
  <img alt="License" src="https://img.shields.io/github/license/containerscrew/cloudsnake">
57
60
  <img alt="Linter" src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json">
61
+ <img alt="PyPip downloads" src="https://img.shields.io/pypi/dm/cloudsnake">
58
62
  </p>
59
63
 
60
64
  ---
61
65
 
66
+ In your terminal, set the corresponding `AWS_PROFILE=MyProfile` if not using the default. (`~/.aws/credentials`). Copy [this helper function](./aws-profile.sh) called `aws-profile` into your favourite shell (`.bashrc`, `.zshrc`, `~/.config/fish/function`) to easily switch between AWS profiles. In case of using `fish` shell, use [this other function](./aws-profile.fish).
67
+
68
+ <br><br>
62
69
  <p align="center">
63
- <img align="center" alt="SSM session" src="docs/img//cloudsnake-ssm-session.gif">
70
+ <img align="center" alt="SSM session" src="docs/img/aws-profile.gif">
71
+ <h3 align="center">aws-profile</h3>
72
+ </p>
73
+
74
+ ---
75
+ <br><br>
76
+ <p align="center">
77
+ <img align="center" alt="SSM session" src="docs/img/cloudsnake-ssm-session.gif">
64
78
  <h3 align="center">SSM session</h3>
65
79
  </p>
66
80
 
67
- In your terminal, set the corresponding `AWS_PROFILE=MyProfile` if not using the default. (`~/.aws/credentials`). Also install the [REQUIRED plugin](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html) to use SSM sessions.
81
+ Install the [REQUIRED plugin](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html) to use SSM sessions.
68
82
 
69
- Then run:
70
83
 
71
84
  ```shell
72
85
  cloudsnake ssm start-session -is # will print all your instances in a terminal menu
@@ -75,15 +88,46 @@ cloudsnake ssm start-session --target i-XXXXXX # connect to the instance specif
75
88
 
76
89
  ---
77
90
 
91
+ <br><br>
78
92
  <p align="center">
79
93
  <img align="center" alt="SSM get parameter" src="docs/img/cloudsnake-ssm-parameter.gif">
80
- <h3 align="center">SSM get parameter</h3>
94
+ <h3 align="center">SSM parameter</h3>
81
95
  </p>
82
96
 
83
97
  ```shell
84
98
  cloudsnake ssm get-parameter # default region eu-west-1
85
99
  cloudsnake --region us-east-1 ssm get-parameters # specify region
86
100
  ```
101
+ ---
102
+
103
+ <br><br>
104
+ <p align="center">
105
+ <img align="center" alt="SSO get-credentials" src="docs/img/cloudsnake-sso-credentials.png">
106
+ <h3 align="center">SSO get-credentials</h3>
107
+ </p>
108
+
109
+ ```shell
110
+ cloudsnake --region eu-west-1 sso get-credentials --start-url https://myapp.awsapps.com/start
111
+ ```
112
+
113
+ > [!NOTE]
114
+ > This command will open your default browser. You will need to approve manually the authentication.
115
+ > More use cases and examples for `cloudsnake sso get-credentials` can be found in [`docs/sso-get-credentials.md`](./docs/sso-get-credentials.md).
116
+
117
+ ---
118
+
119
+ <br><br>
120
+ <p align="center">
121
+ <img align="center" alt="Cloudwatch log stream" src="docs/img/cloudsnake-logs-stream.gif">
122
+ <h3 align="center">Cloudwatch log stream</h3>
123
+ </p>
124
+
125
+ ```shell
126
+ cloudsnake logs stream
127
+ cloudsnake logs stream --filter-pattern "ERROR"
128
+ ```
129
+ > [!NOTE]
130
+ > By the moment, `logs stream` only supports viewing logs from the moment you start the command. Future versions will include the ability to view historical logs.
87
131
 
88
132
  # Installation
89
133
 
@@ -132,14 +176,26 @@ hint: See PEP 668 for the detailed specification.
132
176
  </pre>
133
177
  </details>
134
178
 
179
+ # Upgrade
180
+
181
+ ```bash
182
+ pipx upgrade cloudsnake
183
+ ```
184
+
135
185
  # Uninstall
136
186
 
137
- ```console
187
+ ```bash
138
188
  pipx uninstall cloudsnake
139
189
  # or
140
190
  pip3 uninstall cloudsnake
141
191
  ```
142
192
 
193
+ ## Debug AWS SDK API calls
194
+
195
+ ```shell
196
+ cloudsnake --log-level debug command subcommand [options]
197
+ ```
198
+
143
199
  # License
144
200
 
145
201
  `cloudsnake` is distributed under the terms of the [GPL3](https://spdx.org/licenses/GPL-3.0-or-later.html) license.
@@ -1,6 +1,6 @@
1
1
  <p align="center" >
2
2
  <h3 align="center">cloudsnake 🐍</h3>
3
- <p align="center">Wrapping some useful AWS cli commands to operate AWS like EC2 SSM instance connection or RDS connection using IAM authentication</p>
3
+ <p align="center">Wrapping some useful AWS cli commands to operate some services like EC2, SSO and more</p>
4
4
  </p>
5
5
 
6
6
  ---
@@ -17,18 +17,28 @@
17
17
  <img alt="PyPiP version" src="https://img.shields.io/pypi/v/cloudsnake">
18
18
  <img alt="License" src="https://img.shields.io/github/license/containerscrew/cloudsnake">
19
19
  <img alt="Linter" src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json">
20
+ <img alt="PyPip downloads" src="https://img.shields.io/pypi/dm/cloudsnake">
20
21
  </p>
21
22
 
22
23
  ---
23
24
 
25
+ In your terminal, set the corresponding `AWS_PROFILE=MyProfile` if not using the default. (`~/.aws/credentials`). Copy [this helper function](./aws-profile.sh) called `aws-profile` into your favourite shell (`.bashrc`, `.zshrc`, `~/.config/fish/function`) to easily switch between AWS profiles. In case of using `fish` shell, use [this other function](./aws-profile.fish).
26
+
27
+ <br><br>
24
28
  <p align="center">
25
- <img align="center" alt="SSM session" src="docs/img//cloudsnake-ssm-session.gif">
29
+ <img align="center" alt="SSM session" src="docs/img/aws-profile.gif">
30
+ <h3 align="center">aws-profile</h3>
31
+ </p>
32
+
33
+ ---
34
+ <br><br>
35
+ <p align="center">
36
+ <img align="center" alt="SSM session" src="docs/img/cloudsnake-ssm-session.gif">
26
37
  <h3 align="center">SSM session</h3>
27
38
  </p>
28
39
 
29
- In your terminal, set the corresponding `AWS_PROFILE=MyProfile` if not using the default. (`~/.aws/credentials`). Also install the [REQUIRED plugin](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html) to use SSM sessions.
40
+ Install the [REQUIRED plugin](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html) to use SSM sessions.
30
41
 
31
- Then run:
32
42
 
33
43
  ```shell
34
44
  cloudsnake ssm start-session -is # will print all your instances in a terminal menu
@@ -37,15 +47,46 @@ cloudsnake ssm start-session --target i-XXXXXX # connect to the instance specif
37
47
 
38
48
  ---
39
49
 
50
+ <br><br>
40
51
  <p align="center">
41
52
  <img align="center" alt="SSM get parameter" src="docs/img/cloudsnake-ssm-parameter.gif">
42
- <h3 align="center">SSM get parameter</h3>
53
+ <h3 align="center">SSM parameter</h3>
43
54
  </p>
44
55
 
45
56
  ```shell
46
57
  cloudsnake ssm get-parameter # default region eu-west-1
47
58
  cloudsnake --region us-east-1 ssm get-parameters # specify region
48
59
  ```
60
+ ---
61
+
62
+ <br><br>
63
+ <p align="center">
64
+ <img align="center" alt="SSO get-credentials" src="docs/img/cloudsnake-sso-credentials.png">
65
+ <h3 align="center">SSO get-credentials</h3>
66
+ </p>
67
+
68
+ ```shell
69
+ cloudsnake --region eu-west-1 sso get-credentials --start-url https://myapp.awsapps.com/start
70
+ ```
71
+
72
+ > [!NOTE]
73
+ > This command will open your default browser. You will need to approve manually the authentication.
74
+ > More use cases and examples for `cloudsnake sso get-credentials` can be found in [`docs/sso-get-credentials.md`](./docs/sso-get-credentials.md).
75
+
76
+ ---
77
+
78
+ <br><br>
79
+ <p align="center">
80
+ <img align="center" alt="Cloudwatch log stream" src="docs/img/cloudsnake-logs-stream.gif">
81
+ <h3 align="center">Cloudwatch log stream</h3>
82
+ </p>
83
+
84
+ ```shell
85
+ cloudsnake logs stream
86
+ cloudsnake logs stream --filter-pattern "ERROR"
87
+ ```
88
+ > [!NOTE]
89
+ > By the moment, `logs stream` only supports viewing logs from the moment you start the command. Future versions will include the ability to view historical logs.
49
90
 
50
91
  # Installation
51
92
 
@@ -94,14 +135,26 @@ hint: See PEP 668 for the detailed specification.
94
135
  </pre>
95
136
  </details>
96
137
 
138
+ # Upgrade
139
+
140
+ ```bash
141
+ pipx upgrade cloudsnake
142
+ ```
143
+
97
144
  # Uninstall
98
145
 
99
- ```console
146
+ ```bash
100
147
  pipx uninstall cloudsnake
101
148
  # or
102
149
  pip3 uninstall cloudsnake
103
150
  ```
104
151
 
152
+ ## Debug AWS SDK API calls
153
+
154
+ ```shell
155
+ cloudsnake --log-level debug command subcommand [options]
156
+ ```
157
+
105
158
  # License
106
159
 
107
160
  `cloudsnake` is distributed under the terms of the [GPL3](https://spdx.org/licenses/GPL-3.0-or-later.html) license.
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "cloudsnake"
7
- version = "0.6.0"
7
+ version = "0.10.0"
8
8
  description = 'Some AWS CLI commands with a beautiful TUI'
9
9
  authors = ["containerscrew <info@containerscrew.com>"]
10
10
  repository = "https://github.com/containerscrew/cloudsnake"
@@ -26,7 +26,7 @@ classifiers = [
26
26
  "Programming Language :: Python :: Implementation :: PyPy",
27
27
  ]
28
28
 
29
- include = [{ path = "tests", format = "sdist" }, { path = "CHANGELOG.md" }]
29
+ include = [{ path = "tests", format = "sdist" }, { path = "CHANGELOG.md" }, { path = "src/cloudsnake/styles/*.tcss"}]
30
30
 
31
31
  [tool.poetry.scripts]
32
32
  cloudsnake = 'cloudsnake.__main__:main'
@@ -41,13 +41,16 @@ pytest-cov = "^7.0.0"
41
41
 
42
42
  [tool.poetry.dependencies]
43
43
  python = "^3.12"
44
- boto3 = "^1.41.5"
44
+ boto3 = "^1.42.10"
45
45
  click = "<8.4.0"
46
46
  dacite = "^1.8.1"
47
47
  simple-term-menu = "^1.6.4"
48
- typer = "^0.20.0"
48
+ typer = ">=0.20,<0.22"
49
49
  typing-extensions = "^4.12.0"
50
50
  rich = "^14.2.0"
51
51
  jmespath = "^1.0.1"
52
52
  moto = "^5.0.9"
53
- requests = "^2.32.3"
53
+ requests = "^2.32.5"
54
+ boto3-stubs = "~=1.42.8"
55
+ configparser = "^7.2.0"
56
+ textual = "^7.2.0"
@@ -2,8 +2,12 @@ import os
2
2
  import typer
3
3
  from typing import Optional
4
4
  from importlib.metadata import version
5
+
6
+
5
7
  from cloudsnake.cli.dto import Common, LoggingLevel
8
+ from cloudsnake.cli.logs import cw_logs
6
9
  from cloudsnake.cli.ssm import ssm
10
+ from cloudsnake.cli.sso import sso
7
11
  from cloudsnake.sdk.session import SessionWrapper
8
12
  from cloudsnake.logger import init_logger
9
13
  from rich import traceback
@@ -16,7 +20,7 @@ APP_VERSION = version("cloudsnake")
16
20
  # Declare app and add subcommands
17
21
  app = typer.Typer(
18
22
  name="cloudsnake",
19
- help="🐍☁ A modern CLI to interact with AWS resources (EC2, SSM, RDS). By github.com/containerscrew",
23
+ help=f"🐍 A modern CLI to interact with AWS resources. (c) 2025 containerscrew - version {APP_VERSION}",
20
24
  no_args_is_help=True,
21
25
  pretty_exceptions_short=True,
22
26
  pretty_exceptions_show_locals=False,
@@ -24,17 +28,8 @@ app = typer.Typer(
24
28
  )
25
29
 
26
30
  app.add_typer(ssm, name="ssm", help="Manage SSM operations")
27
-
28
-
29
- @app.command("version", help="Show cloudsnake app version")
30
- def version_cmd():
31
- typer.echo(
32
- typer.style(
33
- f"cloudsnake version: {APP_VERSION}",
34
- fg=typer.colors.GREEN,
35
- bold=True,
36
- )
37
- )
31
+ app.add_typer(sso, name="sso", help="Manage SSO operations")
32
+ app.add_typer(cw_logs, name="logs", help="Manage CloudWatch Logs operations")
38
33
 
39
34
 
40
35
  @app.callback()
@@ -63,7 +58,14 @@ def entrypoint(
63
58
  Entry point for the cloudsnake CLI.
64
59
  """
65
60
  logger = init_logger(log_level.value)
66
- logger.info("Initializing cloudsnake 🐍☁")
61
+
62
+ typer.echo(
63
+ typer.style(
64
+ f"~> cloudsnake 🐍 - version {APP_VERSION}",
65
+ fg=typer.colors.CYAN,
66
+ bold=True,
67
+ )
68
+ )
67
69
 
68
70
  # Create resources
69
71
  session = SessionWrapper(profile, region).with_local_session()
@@ -10,6 +10,19 @@ class Common:
10
10
  region: str
11
11
 
12
12
 
13
+ @dataclass
14
+ class DeviceRegistration:
15
+ client_id: str
16
+ client_secret: str
17
+
18
+
19
+ @dataclass
20
+ class DeviceCode:
21
+ device_code: str
22
+ user_code: str
23
+ verification_uri_complete: str
24
+
25
+
13
26
  class OutputMode(str, Enum):
14
27
  json = "json"
15
28
  table = "table"
@@ -0,0 +1,74 @@
1
+ import signal
2
+ import sys
3
+ import typer
4
+
5
+ from cloudsnake.helpers import (
6
+ log_groups_to_items,
7
+ normalize_log_group_arn_for_live_tail,
8
+ )
9
+ from cloudsnake.sdk.cloudwatch import CloudWatchLogsWrapper, print_colored_log
10
+ from cloudsnake.tui_v2 import SelectorApp
11
+
12
+ DEFAULT_LOG_FILTER = "[].{logGroupName: logGroupName, size: storedBytes, arn: arn}"
13
+
14
+
15
+ def signal_handler(sig, frame):
16
+ typer.secho("You pressed Ctrl+C! Exiting gracefully...", fg="bright_red")
17
+ sys.exit(0)
18
+
19
+
20
+ cw_logs = typer.Typer(
21
+ no_args_is_help=True,
22
+ pretty_exceptions_short=True,
23
+ pretty_exceptions_show_locals=False,
24
+ )
25
+
26
+
27
+ @cw_logs.command(
28
+ "stream", help="Live stream logs from a CloudWatch log group", no_args_is_help=False
29
+ )
30
+ def start_session(
31
+ ctx: typer.Context,
32
+ filter_pattern: str = typer.Option(
33
+ None,
34
+ "--filter-pattern",
35
+ "-f",
36
+ help="Filter pattern for log events",
37
+ ),
38
+ ):
39
+ signal.signal(signal.SIGINT, signal_handler)
40
+ cw = CloudWatchLogsWrapper(
41
+ session=ctx.obj.session,
42
+ profile=ctx.obj.profile,
43
+ region=ctx.obj.region,
44
+ query=DEFAULT_LOG_FILTER,
45
+ )
46
+ log_groups = cw.list_log_groups()
47
+ items = log_groups_to_items(log_groups)
48
+ app = SelectorApp(
49
+ items=items,
50
+ title=f"🚀 AWS Cloudwatch log groups — {ctx.obj.profile}",
51
+ placeholder="Type to filter log groups...",
52
+ )
53
+ selected_log_group = app.run()
54
+
55
+ if selected_log_group:
56
+ log_group_arn = next(
57
+ g["arn"] for g in log_groups if g["logGroupName"] == selected_log_group
58
+ )
59
+ typer.secho(
60
+ f"~> Starting live tail for log group: {selected_log_group}",
61
+ fg="bright_green",
62
+ )
63
+ try:
64
+ for event in cw.tail_log_group_live(
65
+ log_group_arn=normalize_log_group_arn_for_live_tail(log_group_arn),
66
+ filter_pattern=filter_pattern,
67
+ ):
68
+ print_colored_log(event, highlight_term=filter_pattern)
69
+ except KeyboardInterrupt:
70
+ typer.secho("\nStopped.", fg="bright_yellow")
71
+ else:
72
+ typer.secho("~> No log group selected", fg="bright_yellow")
73
+ raise typer.Exit(1)
74
+ return
@@ -0,0 +1,135 @@
1
+ import signal
2
+ import sys
3
+ from typing import Optional
4
+ from cloudsnake.helpers import ec2_targets_to_items, ssm_parameters_to_items
5
+ from cloudsnake.sdk.ssm_parameters import SSMParameterStoreWrapper
6
+ from cloudsnake.tui import EC2Tui, SSMTui
7
+ import typer
8
+
9
+ from cloudsnake.sdk.ec2 import EC2InstanceWrapper
10
+ from cloudsnake.sdk.ssm_session import SSMStartSessionWrapper
11
+ from cloudsnake.tui_v2 import SelectorApp
12
+
13
+ EC2_RUNNING_FILTER = "Name=instance-state-name,Values=running"
14
+
15
+ EC2_INSTANCE_SELECTOR_QUERY = "[].{TargetId: InstanceId, Name: Tags[?Key=='Name'].Value | [0], Ip: PrivateIpAddress}"
16
+
17
+ # TUI
18
+ ec2_tui = EC2Tui()
19
+ ssm_tui = SSMTui()
20
+
21
+
22
+ def signal_handler(sig, frame):
23
+ typer.secho("You pressed Ctrl+C! Exiting gracefully...", fg="bright_red")
24
+ sys.exit(0)
25
+
26
+
27
+ ssm = typer.Typer(
28
+ no_args_is_help=True,
29
+ pretty_exceptions_short=True,
30
+ pretty_exceptions_show_locals=False,
31
+ )
32
+
33
+
34
+ @ssm.command(
35
+ "start-session", help="Start session with the given target id", no_args_is_help=True
36
+ )
37
+ def start_session(
38
+ ctx: typer.Context,
39
+ target: Optional[str] = typer.Option(None, help="Target id of the instance"),
40
+ reason: Optional[str] = typer.Option(
41
+ "ssm-new-connection", help="Reason of the connection"
42
+ ),
43
+ with_instance_selector: Optional[bool] = typer.Option(
44
+ False,
45
+ "--with-instance-selector",
46
+ "-is",
47
+ help="Prompt a terminal menu and select the instance you want to connect. --target flag is no longer used",
48
+ ),
49
+ ):
50
+ signal.signal(signal.SIGINT, signal_handler)
51
+ ssm_wrapper = SSMStartSessionWrapper(
52
+ session=ctx.obj.session,
53
+ profile=ctx.obj.profile,
54
+ region=ctx.obj.region,
55
+ )
56
+
57
+ if with_instance_selector:
58
+ ec2 = EC2InstanceWrapper(
59
+ session=ctx.obj.session,
60
+ filters=EC2_RUNNING_FILTER,
61
+ query=EC2_INSTANCE_SELECTOR_QUERY,
62
+ profile=ctx.obj.profile,
63
+ region=ctx.obj.region,
64
+ )
65
+
66
+ # Fake data for testing
67
+ # instances = [
68
+ # {"TargetId": "i-003a434fb9c00f0f8", "Name": "WebServer-01", "Ip": "10.100.0.2"},
69
+ # {"TargetId": "i-0c7cca12079e449a5", "Name": "eks-instance-nodegroup-apps", "Ip": "10.100.0.3"},
70
+ # {"TargetId": "i-06ad6856a7ca778c6", "Name": "Database-Primary", "Ip": "10.100.0.4"},
71
+ # {"TargetId": "i-050ed4067698e7d26", "Name": "Cache-Server-01", "Ip": "10.100.0.5"},
72
+ # ]
73
+
74
+ instances = ec2.describe_ec2_instances()
75
+
76
+ if not instances:
77
+ typer.secho("~> No running instances found", fg="bright_yellow")
78
+ raise typer.Exit(1)
79
+
80
+ items = ec2_targets_to_items(instances)
81
+
82
+ app = SelectorApp(
83
+ items=items,
84
+ title=f"🚀 EC2 Instances — {ctx.obj.profile}",
85
+ placeholder="Type to filter by name or ID...",
86
+ )
87
+
88
+ result_id = app.run()
89
+
90
+ if result_id:
91
+ selected = next(item for item in instances if item["TargetId"] == result_id)
92
+ instance_id = selected["TargetId"]
93
+ return ssm_wrapper.start_session(instance_id, reason)
94
+ else:
95
+ typer.secho("~> No instance selected", fg="bright_yellow")
96
+ raise typer.Exit(1)
97
+
98
+ return ssm_wrapper.start_session(target, reason)
99
+
100
+
101
+ @ssm.command("get-parameters", help="Get secrets from parameter store")
102
+ def get_parameters(
103
+ ctx: typer.Context,
104
+ ):
105
+ signal.signal(signal.SIGINT, signal_handler)
106
+ ssm_wrapper = SSMParameterStoreWrapper(
107
+ session=ctx.obj.session,
108
+ profile=ctx.obj.profile,
109
+ region=ctx.obj.region,
110
+ )
111
+
112
+ parameters = ssm_wrapper.describe_parameters()
113
+
114
+ # fake_parameters= [
115
+ # {"Name": "/myapp/db_password", "Type": "SecureString"},
116
+ # {"Name": "/myapp/db_endpoint", "Type": "SecureString"},
117
+ # {"Name": "/myapp/redis_password", "Type": "SecureString"},
118
+ # {"Name": "/myapp/google_oauth", "Type": "SecureString"},
119
+ # ]
120
+
121
+ items = ssm_parameters_to_items(parameters)
122
+
123
+ if not parameters:
124
+ typer.echo("No parameters found.")
125
+ raise typer.Exit(1)
126
+
127
+ app = SelectorApp(
128
+ items=items,
129
+ title=f"🚀 SSM Parameter — {ctx.obj.profile}",
130
+ placeholder="Type to filter by name ...",
131
+ )
132
+
133
+ result_id = app.run()
134
+ parameter = ssm_wrapper.get_parameter_by_name(result_id)
135
+ typer.secho(f"~> {parameter}", fg="bright_green")