cloudx-proxy 0.3.9__py3-none-any.whl → 0.3.11__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 CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.3.9'
16
- __version_tuple__ = version_tuple = (0, 3, 9)
15
+ __version__ = version = '0.3.11'
16
+ __version_tuple__ = version_tuple = (0, 3, 11)
cloudx_proxy/cli.py CHANGED
@@ -2,13 +2,13 @@ import os
2
2
  import sys
3
3
  import click
4
4
  from . import __version__
5
- from .core import CloudXClient
5
+ from .core import CloudXProxy
6
6
  from .setup import CloudXSetup
7
7
 
8
8
  @click.group()
9
9
  @click.version_option(version=__version__)
10
10
  def cli():
11
- """CloudX Client - Connect to EC2 instances via SSM for VSCode Remote SSH."""
11
+ """cloudx-proxy - SSH proxy to connect VSCode Remote SSH to EC2 instances using SSM."""
12
12
  pass
13
13
 
14
14
  @cli.command()
@@ -29,7 +29,7 @@ def connect(instance_id: str, port: int, profile: str, region: str, ssh_key: str
29
29
  cloudx-proxy i-0123456789abcdef0 22 --aws-env prod
30
30
  """
31
31
  try:
32
- client = CloudXClient(
32
+ client = CloudXProxy(
33
33
  instance_id=instance_id,
34
34
  port=port,
35
35
  profile=profile,
cloudx_proxy/core.py CHANGED
@@ -5,7 +5,7 @@ from pathlib import Path
5
5
  import boto3
6
6
  from botocore.exceptions import ClientError
7
7
 
8
- class CloudXClient:
8
+ class CloudXProxy:
9
9
  def __init__(self, instance_id: str, port: int = 22, profile: str = "vscode",
10
10
  region: str = None, ssh_key: str = "vscode", aws_env: str = None):
11
11
  """Initialize CloudX client for SSH tunneling via AWS SSM.
cloudx_proxy/setup.py CHANGED
@@ -357,166 +357,87 @@ Host cloudx-{cloudx_env}-{hostname}
357
357
  return True
358
358
  return False
359
359
 
360
- def check_instance_setup(self, instance_id: str) -> Tuple[bool, bool, bool]:
361
- """Check if instance setup is complete.
360
+ def check_instance_setup(self, instance_id: str, hostname: str, cloudx_env: str) -> bool:
361
+ """Check if instance is accessible via SSH.
362
362
 
363
363
  Args:
364
364
  instance_id: EC2 instance ID
365
+ hostname: Hostname for the instance
366
+ cloudx_env: CloudX environment
365
367
 
366
368
  Returns:
367
- Tuple[bool, bool, bool]: (ssm_accessible, is_running, is_setup_complete)
369
+ bool: True if instance is accessible
368
370
  """
371
+ ssh_host = f"cloudx-{cloudx_env}-{hostname}"
372
+ self.print_status(f"Checking SSH connection to {ssh_host}...", None, 4)
373
+
369
374
  try:
370
- session = boto3.Session(profile_name=self.profile)
371
- ssm = session.client('ssm')
372
- ec2 = session.client('ec2')
375
+ # Try to connect with a simple command that will exit immediately
376
+ result = subprocess.run(
377
+ ['ssh', ssh_host, 'exit'],
378
+ capture_output=True,
379
+ text=True,
380
+ timeout=10 # 10 second timeout
381
+ )
373
382
 
374
- # Check instance status via SSM
375
- try:
376
- self.print_status("Checking SSM connectivity...", None, 4)
377
- response = ssm.describe_instance_information(
378
- Filters=[{'Key': 'InstanceIds', 'Values': [instance_id]}]
379
- )
380
- instance_info = response['InstanceInformationList']
381
- if not instance_info:
382
- self.print_status("Instance not accessible via SSM", False, 4)
383
- self.print_status("This could mean the instance is stopped or still configuring", None, 4)
384
- return True, False, False
385
-
386
- # Check instance status from SSM
387
- instance = instance_info[0]
388
- ping_status = instance.get('PingStatus', '')
389
- if ping_status != 'Online':
390
- self.print_status(f"Instance SSM status: {ping_status}", False, 4)
391
- return True, False, False
392
-
393
- self.print_status("Instance is running and SSM connection established", True, 4)
394
-
395
- # Check setup status using SSM command
396
- self.print_status("Checking setup status...", None, 4)
397
- response = ssm.send_command(
398
- InstanceIds=[instance_id],
399
- DocumentName='AWS-RunShellScript',
400
- Parameters={
401
- 'commands': [
402
- 'test -f /home/ec2-user/.install-done && echo "DONE" || '
403
- 'test -f /home/ec2-user/.install-running && echo "RUNNING" || '
404
- 'echo "NOT_STARTED"'
405
- ]
406
- }
407
- )
408
-
409
- command_id = response['Command']['CommandId']
410
-
411
- # Wait for command completion
412
- for _ in range(10): # 10 second timeout
413
- time.sleep(1)
414
- result = ssm.get_command_invocation(
415
- CommandId=command_id,
416
- InstanceId=instance_id
417
- )
418
- if result['Status'] in ['Success', 'Failed']:
419
- break
420
-
421
- is_setup_complete = result['Status'] == 'Success' and result['StandardOutputContent'].strip() == 'DONE'
422
-
423
- if is_setup_complete:
424
- self.print_status("Setup is complete", True, 4)
383
+ if result.returncode == 0:
384
+ self.print_status("SSH connection successful", True, 4)
385
+ return True
386
+ else:
387
+ self.print_status("SSH connection failed", False, 4)
388
+ if "Connection refused" in result.stderr:
389
+ self.print_status("Instance appears to be starting up. Please try again in a few minutes.", None, 4)
390
+ elif "Connection timed out" in result.stderr:
391
+ self.print_status("Instance may be stopped. Please start it through the appropriate channels.", None, 4)
425
392
  else:
426
- status = result['StandardOutputContent'].strip()
427
- self.print_status(f"Setup status: {status}", None, 4)
428
-
429
- return True, True, is_setup_complete
393
+ self.print_status(f"Error: {result.stderr.strip()}", None, 4)
394
+ return False
430
395
 
431
- except Exception as e:
432
- self.print_status(f"Error checking SSM status: {e}", False, 4)
433
- return False, True, False
434
-
396
+ except subprocess.TimeoutExpired:
397
+ self.print_status("SSH connection timed out", False, 4)
398
+ self.print_status("Instance may be stopped or still starting up", None, 4)
399
+ return False
435
400
  except Exception as e:
436
- self.print_status(f"\033[1;91mError:\033[0m {str(e)}", False, 4)
437
- return False, False, False
401
+ self.print_status(f"Error checking SSH connection: {str(e)}", False, 4)
402
+ return False
438
403
 
439
- def wait_for_setup_completion(self, instance_id: str) -> bool:
440
- """Wait for instance setup to complete.
404
+ def wait_for_setup_completion(self, instance_id: str, hostname: str, cloudx_env: str) -> bool:
405
+ """Wait for instance to become accessible via SSH.
441
406
 
442
407
  Args:
443
408
  instance_id: EC2 instance ID
409
+ hostname: Hostname for the instance
410
+ cloudx_env: CloudX environment
444
411
 
445
412
  Returns:
446
- bool: True if setup completed successfully or user chose to continue
413
+ bool: True if instance is accessible or user chose to continue
447
414
  """
448
- self.print_header("Instance Setup Check")
449
- self.print_status(f"Checking instance {instance_id} status...")
415
+ self.print_header("Instance Access Check")
450
416
 
451
- ssm_accessible, is_running, is_complete = self.check_instance_setup(instance_id)
452
-
453
- if not ssm_accessible and not is_running:
454
- continue_setup = self.prompt("Would you like to continue anyway?", "Y").lower() != 'n'
455
- if continue_setup:
456
- self.print_status("Continuing setup despite instance access issues", None, 2)
457
- return True
458
- return False
459
-
460
- if not is_running:
461
- start_instance = self.prompt("Would you like to start the instance?", "Y").lower() != 'n'
462
- if not start_instance:
463
- return False
464
-
465
- self.print_status("Cannot directly start the instance. Please start it through the appropriate channels.", False, 2)
466
- self.print_status("Once started, run this command again to configure SSH access.", None, 2)
467
- return False
468
-
469
- if is_complete:
470
- self.print_status("Instance setup is complete", True, 2)
417
+ if self.check_instance_setup(instance_id, hostname, cloudx_env):
471
418
  return True
472
-
473
- if not ssm_accessible:
474
- self.print_status("Waiting for SSM access...", None, 2)
475
- # Wait for SSM access
476
- for _ in range(30): # 5 minute timeout
477
- time.sleep(10)
478
- ssm_accessible, is_running, is_complete = self.check_instance_setup(instance_id)
479
- if ssm_accessible or not is_running:
480
- break
481
419
 
482
- if not ssm_accessible:
483
- self.print_status("Timeout waiting for SSM access", False, 2)
484
- continue_setup = self.prompt("Would you like to continue anyway?", "Y").lower() != 'n'
485
- if continue_setup:
486
- self.print_status("Continuing setup despite SSM access issues", None, 2)
487
- return True
488
- return False
489
-
490
- wait = self.prompt("Instance setup is not complete. Would you like to wait?", "Y").lower() != 'n'
420
+ wait = self.prompt("Would you like to wait for the instance to become accessible?", "Y").lower() != 'n'
491
421
  if not wait:
492
- self.print_status("Skipping instance setup check", None, 2)
493
- return True
422
+ return False
494
423
 
495
- self.print_status("Waiting for setup to complete...", None, 2)
424
+ self.print_status("Waiting for SSH access...", None, 2)
496
425
  dots = 0
497
- while True:
498
- ssm_accessible, is_running, is_complete = self.check_instance_setup(instance_id)
499
-
500
- if not is_running:
501
- self.print_status("Instance is no longer running", False, 2)
502
- continue_setup = self.prompt("Would you like to continue anyway?", "Y").lower() != 'n'
503
- if continue_setup:
504
- self.print_status("Continuing setup despite instance issues", None, 2)
505
- return True
506
- return False
507
-
508
- if not ssm_accessible:
509
- self.print_status("Lost SSM access to instance", False, 2)
510
- continue_setup = self.prompt("Would you like to continue anyway?", "Y").lower() != 'n'
511
- if continue_setup:
512
- self.print_status("Continuing setup despite SSM access issues", None, 2)
513
- return True
514
- return False
515
-
516
- if is_complete:
517
- self.print_status("Instance setup completed successfully", True, 2)
426
+ attempts = 0
427
+ max_attempts = 30 # 5 minute timeout (10 seconds * 30)
428
+
429
+ while attempts < max_attempts:
430
+ if self.check_instance_setup(instance_id, hostname, cloudx_env):
518
431
  return True
519
432
 
520
433
  dots = (dots + 1) % 4
521
434
  print(f"\r {'.' * dots}{' ' * (3 - dots)}", end='', flush=True)
522
435
  time.sleep(10)
436
+ attempts += 1
437
+
438
+ self.print_status("Timeout waiting for SSH access", False, 2)
439
+ continue_setup = self.prompt("Would you like to continue anyway?", "Y").lower() != 'n'
440
+ if continue_setup:
441
+ self.print_status("Continuing setup despite SSH access issues", None, 2)
442
+ return True
443
+ return False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: cloudx-proxy
3
- Version: 0.3.9
3
+ Version: 0.3.11
4
4
  Summary: SSH proxy command to connect VSCode with Cloud9/CloudX instance using AWS Systems Manager
5
5
  Author-email: easytocloud <info@easytocloud.com>
6
6
  License: MIT License
@@ -0,0 +1,11 @@
1
+ cloudx_proxy/__init__.py,sha256=ZZ2O_m9OFJm18AxMSuYJt4UjSuSqyJlYRaZMoets498,61
2
+ cloudx_proxy/_version.py,sha256=cgZZwxO2Czf2RAwPc5Ugf8lBeLEkSyVqWm7FWjhlv2A,413
3
+ cloudx_proxy/cli.py,sha256=ni_CP55dW-K_Ei59KpV4xcyklX1K1DUCgwdLzI2ipIk,3434
4
+ cloudx_proxy/core.py,sha256=XQbVlPaqQQ352Ao_JJlN-PpqIdQtSBec0lNRB1s0JSk,7288
5
+ cloudx_proxy/setup.py,sha256=GNgZcUnnJG7q7npUl4QW_4mWfJT0gKZ_i1fwmxj3pmI,18672
6
+ cloudx_proxy-0.3.11.dist-info/LICENSE,sha256=i7P2OR4zsJYsMWcCUDe_B9ZfGi9bU0K5I2nKfDrW_N8,1068
7
+ cloudx_proxy-0.3.11.dist-info/METADATA,sha256=VoCtFzwC-ZkGbZInbj81EMq072Kkt0rsUVKyVcJRXGg,14038
8
+ cloudx_proxy-0.3.11.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
9
+ cloudx_proxy-0.3.11.dist-info/entry_points.txt,sha256=HGt743N2lVlKd7O1qWq3C0aEHyS5PjPnxzDHh7hwtSg,54
10
+ cloudx_proxy-0.3.11.dist-info/top_level.txt,sha256=2wtEote1db21j-VvkCJFfT-dLlauuG5indjggYh3xDg,13
11
+ cloudx_proxy-0.3.11.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- cloudx_proxy/__init__.py,sha256=ZZ2O_m9OFJm18AxMSuYJt4UjSuSqyJlYRaZMoets498,61
2
- cloudx_proxy/_version.py,sha256=nV2HEiFwTdaOZoFEyVxxG_D8Oq_nlSmX2vHL4jK4h6w,411
3
- cloudx_proxy/cli.py,sha256=Ph-m8lDsdU2zZab9Y6YgBBzd_UDouBnfNrYFFx0bI_E,3426
4
- cloudx_proxy/core.py,sha256=WjKoqMmmnt6e_4JMeq4gTka75JAvQcMUs9r9XUBLmFE,7289
5
- cloudx_proxy/setup.py,sha256=dj8YQgCI03J03inv_b8dDhch8KHcI3hYQWYQqpr9azs,22540
6
- cloudx_proxy-0.3.9.dist-info/LICENSE,sha256=i7P2OR4zsJYsMWcCUDe_B9ZfGi9bU0K5I2nKfDrW_N8,1068
7
- cloudx_proxy-0.3.9.dist-info/METADATA,sha256=S7tYMFC9-izIgqTYOKrpJyde9VNmMojdopz9TaAh30c,14037
8
- cloudx_proxy-0.3.9.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
9
- cloudx_proxy-0.3.9.dist-info/entry_points.txt,sha256=HGt743N2lVlKd7O1qWq3C0aEHyS5PjPnxzDHh7hwtSg,54
10
- cloudx_proxy-0.3.9.dist-info/top_level.txt,sha256=2wtEote1db21j-VvkCJFfT-dLlauuG5indjggYh3xDg,13
11
- cloudx_proxy-0.3.9.dist-info/RECORD,,