cloudx-proxy 0.4.5__py3-none-any.whl → 0.4.6__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.
- cloudx_proxy/_version.py +2 -2
- cloudx_proxy/cli.py +15 -4
- cloudx_proxy/setup.py +69 -44
- {cloudx_proxy-0.4.5.dist-info → cloudx_proxy-0.4.6.dist-info}/METADATA +1 -1
- cloudx_proxy-0.4.6.dist-info/RECORD +12 -0
- cloudx_proxy-0.4.5.dist-info/RECORD +0 -12
- {cloudx_proxy-0.4.5.dist-info → cloudx_proxy-0.4.6.dist-info}/LICENSE +0 -0
- {cloudx_proxy-0.4.5.dist-info → cloudx_proxy-0.4.6.dist-info}/WHEEL +0 -0
- {cloudx_proxy-0.4.5.dist-info → cloudx_proxy-0.4.6.dist-info}/entry_points.txt +0 -0
- {cloudx_proxy-0.4.5.dist-info → cloudx_proxy-0.4.6.dist-info}/top_level.txt +0 -0
cloudx_proxy/_version.py
CHANGED
cloudx_proxy/cli.py
CHANGED
@@ -55,7 +55,10 @@ def connect(instance_id: str, port: int, profile: str, region: str, ssh_key: str
|
|
55
55
|
@click.option('--ssh-config', help='SSH config file to use (default: ~/.ssh/vscode/config)')
|
56
56
|
@click.option('--aws-env', help='AWS environment directory (default: ~/.aws, use name of directory in ~/.aws/aws-envs/)')
|
57
57
|
@click.option('--1password', 'use_1password', is_flag=True, help='Use 1Password SSH agent for SSH authentication')
|
58
|
-
|
58
|
+
@click.option('--instance', help='EC2 instance ID to set up connection for')
|
59
|
+
@click.option('--yes', 'non_interactive', is_flag=True, help='Non-interactive mode, use default values for all prompts')
|
60
|
+
def setup(profile: str, ssh_key: str, ssh_config: str, aws_env: str, use_1password: bool,
|
61
|
+
instance: str, non_interactive: bool):
|
59
62
|
"""Set up AWS profile, SSH keys, and configuration for CloudX.
|
60
63
|
|
61
64
|
This command will:
|
@@ -69,6 +72,7 @@ def setup(profile: str, ssh_key: str, ssh_config: str, aws_env: str, use_1passwo
|
|
69
72
|
cloudx-proxy setup --profile myprofile --ssh-key mykey
|
70
73
|
cloudx-proxy setup --ssh-config ~/.ssh/cloudx/config
|
71
74
|
cloudx-proxy setup --1password
|
75
|
+
cloudx-proxy setup --instance i-0123456789abcdef0 --yes
|
72
76
|
"""
|
73
77
|
try:
|
74
78
|
setup = CloudXSetup(
|
@@ -76,7 +80,9 @@ def setup(profile: str, ssh_key: str, ssh_config: str, aws_env: str, use_1passwo
|
|
76
80
|
ssh_key=ssh_key,
|
77
81
|
ssh_config=ssh_config,
|
78
82
|
aws_env=aws_env,
|
79
|
-
use_1password=use_1password
|
83
|
+
use_1password=use_1password,
|
84
|
+
instance_id=instance,
|
85
|
+
non_interactive=non_interactive
|
80
86
|
)
|
81
87
|
|
82
88
|
print("\n\033[1;95m=== cloudx-proxy Setup ===\033[0m\n")
|
@@ -91,8 +97,13 @@ def setup(profile: str, ssh_key: str, ssh_config: str, aws_env: str, use_1passwo
|
|
91
97
|
|
92
98
|
# Get environment and instance details
|
93
99
|
cloudx_env = setup.prompt("Enter environment", getattr(setup, 'default_env', None))
|
94
|
-
|
95
|
-
|
100
|
+
|
101
|
+
# Use the --instance parameter if provided, otherwise prompt
|
102
|
+
instance_id = instance or setup.prompt("Enter EC2 instance ID (e.g., i-0123456789abcdef0)")
|
103
|
+
|
104
|
+
# Generate a default hostname based on instance ID if we're in non-interactive mode
|
105
|
+
hostname_default = f"instance-{instance_id[-7:]}" if non_interactive else None
|
106
|
+
hostname = setup.prompt("Enter hostname for the instance", hostname_default)
|
96
107
|
|
97
108
|
# Set up SSH config
|
98
109
|
if not setup.setup_ssh_config(cloudx_env, instance_id, hostname):
|
cloudx_proxy/setup.py
CHANGED
@@ -10,8 +10,12 @@ from botocore.exceptions import ClientError
|
|
10
10
|
from ._1password import check_1password_cli, check_ssh_agent, list_ssh_keys, create_ssh_key, get_vaults, save_public_key
|
11
11
|
|
12
12
|
class CloudXSetup:
|
13
|
+
# Define SSH key prefix as a constant
|
14
|
+
SSH_KEY_PREFIX = "cloudX SSH Key - "
|
15
|
+
|
13
16
|
def __init__(self, profile: str = "vscode", ssh_key: str = "vscode", ssh_config: str = None,
|
14
|
-
aws_env: str = None, use_1password: bool = False
|
17
|
+
aws_env: str = None, use_1password: bool = False, instance_id: str = None,
|
18
|
+
non_interactive: bool = False):
|
15
19
|
"""Initialize cloudx-proxy setup.
|
16
20
|
|
17
21
|
Args:
|
@@ -20,11 +24,15 @@ class CloudXSetup:
|
|
20
24
|
ssh_config: SSH config file path (default: None, uses ~/.ssh/vscode/config)
|
21
25
|
aws_env: AWS environment directory (default: None)
|
22
26
|
use_1password: Use 1Password SSH agent for authentication (default: False)
|
27
|
+
instance_id: EC2 instance ID to set up connection for (optional)
|
28
|
+
non_interactive: Non-interactive mode, use defaults for all prompts (default: False)
|
23
29
|
"""
|
24
30
|
self.profile = profile
|
25
31
|
self.ssh_key = ssh_key
|
26
32
|
self.aws_env = aws_env
|
27
33
|
self.use_1password = use_1password
|
34
|
+
self.instance_id = instance_id
|
35
|
+
self.non_interactive = non_interactive
|
28
36
|
self.home_dir = str(Path.home())
|
29
37
|
self.onepassword_agent_sock = Path(self.home_dir) / ".1password" / "agent.sock"
|
30
38
|
|
@@ -74,6 +82,16 @@ class CloudXSetup:
|
|
74
82
|
Returns:
|
75
83
|
str: User's input or default value
|
76
84
|
"""
|
85
|
+
# In non-interactive mode, always use the default value
|
86
|
+
if self.non_interactive:
|
87
|
+
if default:
|
88
|
+
self.print_status(f"{message}: Using default [{default}]", None, 2)
|
89
|
+
return default
|
90
|
+
else:
|
91
|
+
self.print_status(f"{message}: No default value available", False, 2)
|
92
|
+
raise ValueError(f"Non-interactive mode requires default value for: {message}")
|
93
|
+
|
94
|
+
# Interactive prompt
|
77
95
|
if default:
|
78
96
|
prompt_text = f"\033[93m{message} [{default}]: \033[0m"
|
79
97
|
else:
|
@@ -200,38 +218,14 @@ class CloudXSetup:
|
|
200
218
|
bool: True if successful
|
201
219
|
"""
|
202
220
|
try:
|
203
|
-
# Get vaults to determine where to store the key
|
204
|
-
vaults = get_vaults()
|
205
|
-
if not vaults:
|
206
|
-
self.print_status("No 1Password vaults found", False, 2)
|
207
|
-
return False
|
208
|
-
|
209
|
-
# Display available vaults
|
210
|
-
self.print_status("Creating a new SSH key in 1Password", None, 2)
|
211
|
-
print("\n\033[96mAvailable 1Password vaults:\033[0m")
|
212
|
-
for i, vault in enumerate(vaults):
|
213
|
-
print(f" {i+1}. {vault['name']}")
|
214
|
-
|
215
|
-
# Let user select vault
|
216
|
-
vault_num = self.prompt("Select vault number to store SSH key", "1")
|
217
|
-
try:
|
218
|
-
vault_idx = int(vault_num) - 1
|
219
|
-
if vault_idx < 0 or vault_idx >= len(vaults):
|
220
|
-
self.print_status("Invalid vault number", False, 2)
|
221
|
-
return False
|
222
|
-
selected_vault = vaults[vault_idx]['id']
|
223
|
-
except ValueError:
|
224
|
-
self.print_status("Invalid input", False, 2)
|
225
|
-
return False
|
226
|
-
|
227
221
|
# Create possible title variations for the 1Password item
|
228
|
-
ssh_key_title_with_prefix = f"
|
222
|
+
ssh_key_title_with_prefix = f"{self.SSH_KEY_PREFIX}{self.ssh_key}"
|
229
223
|
ssh_key_title_without_prefix = self.ssh_key
|
230
224
|
|
231
|
-
#
|
225
|
+
# First check if key exists in any vault
|
232
226
|
ssh_keys = list_ssh_keys()
|
233
227
|
|
234
|
-
#
|
228
|
+
# Check for both prefixed and non-prefixed format
|
235
229
|
existing_key = next((key for key in ssh_keys if key['title'] == ssh_key_title_with_prefix), None)
|
236
230
|
if not existing_key:
|
237
231
|
existing_key = next((key for key in ssh_keys if key['title'] == ssh_key_title_without_prefix), None)
|
@@ -253,25 +247,56 @@ class CloudXSetup:
|
|
253
247
|
if save_public_key(public_key, f"{self.ssh_key_file}.pub"):
|
254
248
|
self.print_status(f"Saved existing public key to {self.ssh_key_file}.pub", True, 2)
|
255
249
|
return True
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
success, public_key, item_id = create_ssh_key(ssh_key_title_with_prefix, selected_vault)
|
260
|
-
|
261
|
-
if not success:
|
262
|
-
self.print_status("Failed to create SSH key in 1Password", False, 2)
|
263
|
-
return False
|
264
|
-
|
265
|
-
self.print_status("SSH key created successfully in 1Password", True, 2)
|
266
|
-
|
267
|
-
# Save the public key to the expected location
|
268
|
-
if save_public_key(public_key, f"{self.ssh_key_file}.pub"):
|
269
|
-
self.print_status(f"Saved public key to {self.ssh_key_file}.pub", True, 2)
|
270
|
-
return True
|
250
|
+
else:
|
251
|
+
self.print_status(f"Failed to save public key to {self.ssh_key_file}.pub", False, 2)
|
252
|
+
return False
|
271
253
|
else:
|
272
|
-
self.print_status(f"Failed to
|
254
|
+
self.print_status(f"Failed to retrieve public key from 1Password", False, 2)
|
273
255
|
return False
|
274
256
|
|
257
|
+
# If we reach here, the key doesn't exist and we need to create it
|
258
|
+
# Get vaults to determine where to store the key
|
259
|
+
vaults = get_vaults()
|
260
|
+
if not vaults:
|
261
|
+
self.print_status("No 1Password vaults found", False, 2)
|
262
|
+
return False
|
263
|
+
|
264
|
+
# Display available vaults
|
265
|
+
self.print_status("Creating a new SSH key in 1Password", None, 2)
|
266
|
+
print("\n\033[96mAvailable 1Password vaults:\033[0m")
|
267
|
+
for i, vault in enumerate(vaults):
|
268
|
+
print(f" {i+1}. {vault['name']}")
|
269
|
+
|
270
|
+
# Let user select vault
|
271
|
+
vault_num = self.prompt("Select vault number to store SSH key", "1")
|
272
|
+
try:
|
273
|
+
vault_idx = int(vault_num) - 1
|
274
|
+
if vault_idx < 0 or vault_idx >= len(vaults):
|
275
|
+
self.print_status("Invalid vault number", False, 2)
|
276
|
+
return False
|
277
|
+
selected_vault = vaults[vault_idx]['id']
|
278
|
+
except ValueError:
|
279
|
+
self.print_status("Invalid input", False, 2)
|
280
|
+
return False
|
281
|
+
|
282
|
+
# Create a new SSH key in 1Password
|
283
|
+
self.print_status(f"Creating new SSH key '{ssh_key_title_with_prefix}' in 1Password...", None, 2)
|
284
|
+
success, public_key, item_id = create_ssh_key(ssh_key_title_with_prefix, selected_vault)
|
285
|
+
|
286
|
+
if not success:
|
287
|
+
self.print_status("Failed to create SSH key in 1Password", False, 2)
|
288
|
+
return False
|
289
|
+
|
290
|
+
self.print_status("SSH key created successfully in 1Password", True, 2)
|
291
|
+
|
292
|
+
# Save the public key to the expected location
|
293
|
+
if save_public_key(public_key, f"{self.ssh_key_file}.pub"):
|
294
|
+
self.print_status(f"Saved public key to {self.ssh_key_file}.pub", True, 2)
|
295
|
+
return True
|
296
|
+
else:
|
297
|
+
self.print_status(f"Failed to save public key to {self.ssh_key_file}.pub", False, 2)
|
298
|
+
return False
|
299
|
+
|
275
300
|
# Remind user to enable the key in 1Password SSH agent
|
276
301
|
self.print_status("\033[93mImportant: Make sure the key is enabled in 1Password's SSH agent settings\033[0m", None, 2)
|
277
302
|
return True
|
@@ -0,0 +1,12 @@
|
|
1
|
+
cloudx_proxy/_1password.py,sha256=uxyCfVvO1eQrOfYRojst_LN2DV4fIwxM5moaQTn3wQY,5853
|
2
|
+
cloudx_proxy/__init__.py,sha256=ZZ2O_m9OFJm18AxMSuYJt4UjSuSqyJlYRaZMoets498,61
|
3
|
+
cloudx_proxy/_version.py,sha256=KTwoycq1UXsZO5AtXG8jzeyeaE1ORqxkwO9Pn0qrlwg,511
|
4
|
+
cloudx_proxy/cli.py,sha256=VqYdcTn_PC-mhcCpNEa_PcpHjV7vxIDzhfxu-WKlDdU,4904
|
5
|
+
cloudx_proxy/core.py,sha256=RF3bX5MQiokRKjYEPnfWdKywGdtoVUvV2xZqm9uOl1g,8135
|
6
|
+
cloudx_proxy/setup.py,sha256=QrInbZdghpAYn064h02KmpA9r-nEbVm-LyTcXLxSmXY,39823
|
7
|
+
cloudx_proxy-0.4.6.dist-info/LICENSE,sha256=i7P2OR4zsJYsMWcCUDe_B9ZfGi9bU0K5I2nKfDrW_N8,1068
|
8
|
+
cloudx_proxy-0.4.6.dist-info/METADATA,sha256=LV5xMR7w2qrmcID563IlI83R7X2SOxzcRo_qKLbBa9k,18522
|
9
|
+
cloudx_proxy-0.4.6.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
10
|
+
cloudx_proxy-0.4.6.dist-info/entry_points.txt,sha256=HGt743N2lVlKd7O1qWq3C0aEHyS5PjPnxzDHh7hwtSg,54
|
11
|
+
cloudx_proxy-0.4.6.dist-info/top_level.txt,sha256=2wtEote1db21j-VvkCJFfT-dLlauuG5indjggYh3xDg,13
|
12
|
+
cloudx_proxy-0.4.6.dist-info/RECORD,,
|
@@ -1,12 +0,0 @@
|
|
1
|
-
cloudx_proxy/_1password.py,sha256=uxyCfVvO1eQrOfYRojst_LN2DV4fIwxM5moaQTn3wQY,5853
|
2
|
-
cloudx_proxy/__init__.py,sha256=ZZ2O_m9OFJm18AxMSuYJt4UjSuSqyJlYRaZMoets498,61
|
3
|
-
cloudx_proxy/_version.py,sha256=YrJwLVAkXjdo87YWYyrI7_B7BOSjbDIJ_FAFmb7g_vc,511
|
4
|
-
cloudx_proxy/cli.py,sha256=5IcfYFACUOa4pqSKuHucqZionI9P8n5ZLvtzyXYeTvw,4218
|
5
|
-
cloudx_proxy/core.py,sha256=RF3bX5MQiokRKjYEPnfWdKywGdtoVUvV2xZqm9uOl1g,8135
|
6
|
-
cloudx_proxy/setup.py,sha256=TMFSo3-RusvQ5G7_I0vUPtZG18yDnogQbRDxGxVtNnM,38632
|
7
|
-
cloudx_proxy-0.4.5.dist-info/LICENSE,sha256=i7P2OR4zsJYsMWcCUDe_B9ZfGi9bU0K5I2nKfDrW_N8,1068
|
8
|
-
cloudx_proxy-0.4.5.dist-info/METADATA,sha256=heC8F4kGgkmgSy-8SdqpgsKsGZ27N3xNkZI8wR3nogw,18522
|
9
|
-
cloudx_proxy-0.4.5.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
10
|
-
cloudx_proxy-0.4.5.dist-info/entry_points.txt,sha256=HGt743N2lVlKd7O1qWq3C0aEHyS5PjPnxzDHh7hwtSg,54
|
11
|
-
cloudx_proxy-0.4.5.dist-info/top_level.txt,sha256=2wtEote1db21j-VvkCJFfT-dLlauuG5indjggYh3xDg,13
|
12
|
-
cloudx_proxy-0.4.5.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|