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.
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/PKG-INFO +66 -10
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/README.md +59 -6
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/pyproject.toml +8 -5
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/cli/cli.py +15 -13
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/cli/dto.py +13 -0
- cloudsnake-0.10.0/src/cloudsnake/cli/logs.py +74 -0
- cloudsnake-0.10.0/src/cloudsnake/cli/ssm.py +135 -0
- cloudsnake-0.10.0/src/cloudsnake/cli/sso.py +155 -0
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/helpers.py +77 -1
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/logger.py +2 -1
- cloudsnake-0.10.0/src/cloudsnake/models/selector.py +9 -0
- cloudsnake-0.10.0/src/cloudsnake/sdk/cloudwatch.py +119 -0
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/sdk/session.py +1 -1
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/sdk/ssm_parameters.py +6 -4
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/sdk/ssm_session.py +4 -4
- cloudsnake-0.10.0/src/cloudsnake/sdk/sso.py +71 -0
- cloudsnake-0.10.0/src/cloudsnake/sdk/sso_oidc.py +65 -0
- cloudsnake-0.10.0/src/cloudsnake/styles/tui.tcss +58 -0
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/tui.py +1 -0
- cloudsnake-0.10.0/src/cloudsnake/tui_v2.py +96 -0
- cloudsnake-0.10.0/src/cloudsnake/utils.py +63 -0
- cloudsnake-0.10.0/tests/test_cli.py +12 -0
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/tests/test_helpers.py +0 -7
- cloudsnake-0.6.0/src/cloudsnake/cli/ssm.py +0 -107
- cloudsnake-0.6.0/tests/test_cli.py +0 -12
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/CHANGELOG.md +0 -0
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/LICENSE +0 -0
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/__init__.py +0 -0
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/__main__.py +0 -0
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/cli/rds.py +0 -0
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/sdk/aws.py +0 -0
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/sdk/ec2.py +0 -0
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/src/cloudsnake/sdk/rds_session.py +0 -0
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/tests/README.md +0 -0
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/tests/__init__.py +0 -0
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/tests/conftest.py +0 -0
- {cloudsnake-0.6.0 → cloudsnake-0.10.0}/tests/test_logger.py +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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:
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
```
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
```
|
|
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.
|
|
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.
|
|
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 = "
|
|
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.
|
|
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="
|
|
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
|
-
|
|
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")
|