cloudx-proxy 0.1.1__tar.gz → 0.2.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 (22) hide show
  1. {cloudx_proxy-0.1.1 → cloudx_proxy-0.2.0}/CHANGELOG.md +7 -0
  2. {cloudx_proxy-0.1.1 → cloudx_proxy-0.2.0}/PKG-INFO +1 -1
  3. {cloudx_proxy-0.1.1 → cloudx_proxy-0.2.0}/cloudx_proxy/_version.py +2 -2
  4. {cloudx_proxy-0.1.1 → cloudx_proxy-0.2.0}/cloudx_proxy/setup.py +108 -28
  5. {cloudx_proxy-0.1.1 → cloudx_proxy-0.2.0}/cloudx_proxy.egg-info/PKG-INFO +1 -1
  6. {cloudx_proxy-0.1.1 → cloudx_proxy-0.2.0}/.github/workflows/release.yml +0 -0
  7. {cloudx_proxy-0.1.1 → cloudx_proxy-0.2.0}/.gitignore +0 -0
  8. {cloudx_proxy-0.1.1 → cloudx_proxy-0.2.0}/.releaserc +0 -0
  9. {cloudx_proxy-0.1.1 → cloudx_proxy-0.2.0}/CONTRIBUTING.md +0 -0
  10. {cloudx_proxy-0.1.1 → cloudx_proxy-0.2.0}/LICENSE +0 -0
  11. {cloudx_proxy-0.1.1 → cloudx_proxy-0.2.0}/README.md +0 -0
  12. {cloudx_proxy-0.1.1 → cloudx_proxy-0.2.0}/cloudx_proxy/__init__.py +0 -0
  13. {cloudx_proxy-0.1.1 → cloudx_proxy-0.2.0}/cloudx_proxy/cli.py +0 -0
  14. {cloudx_proxy-0.1.1 → cloudx_proxy-0.2.0}/cloudx_proxy/core.py +0 -0
  15. {cloudx_proxy-0.1.1 → cloudx_proxy-0.2.0}/cloudx_proxy.egg-info/SOURCES.txt +0 -0
  16. {cloudx_proxy-0.1.1 → cloudx_proxy-0.2.0}/cloudx_proxy.egg-info/dependency_links.txt +0 -0
  17. {cloudx_proxy-0.1.1 → cloudx_proxy-0.2.0}/cloudx_proxy.egg-info/entry_points.txt +0 -0
  18. {cloudx_proxy-0.1.1 → cloudx_proxy-0.2.0}/cloudx_proxy.egg-info/requires.txt +0 -0
  19. {cloudx_proxy-0.1.1 → cloudx_proxy-0.2.0}/cloudx_proxy.egg-info/top_level.txt +0 -0
  20. {cloudx_proxy-0.1.1 → cloudx_proxy-0.2.0}/package.json +0 -0
  21. {cloudx_proxy-0.1.1 → cloudx_proxy-0.2.0}/pyproject.toml +0 -0
  22. {cloudx_proxy-0.1.1 → cloudx_proxy-0.2.0}/setup.cfg +0 -0
@@ -1,3 +1,10 @@
1
+ # [0.2.0](https://github.com/easytocloud/cloudX-proxy/compare/v0.1.1...v0.2.0) (2025-02-09)
2
+
3
+
4
+ ### Features
5
+
6
+ * add setup checklist and make all steps optional ([46016b8](https://github.com/easytocloud/cloudX-proxy/commit/46016b8fd7f1a1ae42fb34a7ff35365279883ab0))
7
+
1
8
  ## [0.1.1](https://github.com/easytocloud/cloudX-proxy/compare/v0.1.0...v0.1.1) (2025-02-09)
2
9
 
3
10
  # Changelog
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: cloudx-proxy
3
- Version: 0.1.1
3
+ Version: 0.2.0
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
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.1.1'
16
- __version_tuple__ = version_tuple = (0, 1, 1)
15
+ __version__ = version = '0.2.0'
16
+ __version_tuple__ = version_tuple = (0, 2, 0)
@@ -25,12 +25,31 @@ class CloudXSetup:
25
25
  self.ssh_key_file = self.ssh_dir / f"{ssh_key}"
26
26
  self.using_1password = False
27
27
 
28
+ def print_status(self, message: str, status: bool = None, indent: int = 0) -> None:
29
+ """Print a status message with optional checkmark/cross.
30
+
31
+ Args:
32
+ message: The message to print
33
+ status: True for success (✓), False for failure (✗), None for no symbol
34
+ indent: Number of spaces to indent
35
+ """
36
+ prefix = " " * indent
37
+ if status is not None:
38
+ symbol = "✓" if status else "✗"
39
+ color = "\033[92m" if status else "\033[91m" # Green for success, red for failure
40
+ reset = "\033[0m"
41
+ print(f"{prefix}{color}{symbol}{reset} {message}")
42
+ else:
43
+ print(f"{prefix}○ {message}")
44
+
28
45
  def setup_aws_profile(self) -> bool:
29
46
  """Set up AWS profile using aws configure command.
30
47
 
31
48
  Returns:
32
- bool: True if profile was set up successfully
49
+ bool: True if profile was set up successfully or user chose to continue
33
50
  """
51
+ self.print_status("Checking AWS profile configuration...")
52
+
34
53
  try:
35
54
  # Configure AWS environment if specified
36
55
  if self.aws_env:
@@ -41,14 +60,25 @@ class CloudXSetup:
41
60
  # Check if profile exists
42
61
  session = boto3.Session(profile_name=self.profile)
43
62
  try:
44
- session.client('sts').get_caller_identity()
45
- print(f"AWS profile '{self.profile}' already exists and is valid.")
63
+ identity = session.client('sts').get_caller_identity()
64
+ user_arn = identity['Arn']
65
+
66
+ if any(part.startswith('cloudX-') for part in user_arn.split('/')):
67
+ self.print_status(f"AWS profile '{self.profile}' exists and matches cloudX format", True, 2)
68
+ else:
69
+ self.print_status(f"AWS profile '{self.profile}' exists but doesn't match cloudX-{{env}}-{{user}} format", False, 2)
46
70
  return True
47
71
  except ClientError:
48
- pass
72
+ self.print_status(f"AWS profile '{self.profile}' not found or invalid", False, 2)
73
+
74
+ # Ask user if they want to set up the profile
75
+ setup_profile = input(f"Would you like to set up AWS profile '{self.profile}'? (Y/n): ").lower() != 'n'
76
+ if not setup_profile:
77
+ self.print_status("Skipping AWS profile setup", None, 2)
78
+ return True
49
79
 
50
80
  # Profile doesn't exist or is invalid, set it up
51
- print(f"Setting up AWS profile '{self.profile}'...")
81
+ self.print_status("Setting up AWS profile...", None, 2)
52
82
  print("Please enter your AWS credentials:")
53
83
 
54
84
  # Use aws configure command
@@ -62,13 +92,19 @@ class CloudXSetup:
62
92
  identity = session.client('sts').get_caller_identity()
63
93
  user_arn = identity['Arn']
64
94
 
65
- if not any(part.startswith('cloudX-') for part in user_arn.split('/')):
66
- print(f"Warning: User ARN '{user_arn}' does not match expected format cloudX-{{env}}-{{user}}")
95
+ if any(part.startswith('cloudX-') for part in user_arn.split('/')):
96
+ self.print_status("AWS profile setup complete and matches cloudX format", True, 2)
97
+ else:
98
+ self.print_status("AWS profile setup complete but doesn't match cloudX-{env}-{user} format", False, 2)
67
99
 
68
100
  return True
69
101
 
70
102
  except Exception as e:
71
- print(f"Error setting up AWS profile: {e}")
103
+ self.print_status(f"Error: {str(e)}", False, 2)
104
+ continue_setup = input("Would you like to continue anyway? (Y/n): ").lower() != 'n'
105
+ if continue_setup:
106
+ self.print_status("Continuing setup despite AWS profile issues", None, 2)
107
+ return True
72
108
  return False
73
109
 
74
110
  def setup_ssh_key(self) -> bool:
@@ -77,38 +113,56 @@ class CloudXSetup:
77
113
  Returns:
78
114
  bool: True if key was set up successfully
79
115
  """
116
+ self.print_status("Checking SSH key configuration...")
117
+
80
118
  try:
81
119
  # Create .ssh/vscode directory if it doesn't exist
82
120
  self.ssh_dir.mkdir(parents=True, exist_ok=True)
121
+ self.print_status("SSH directory exists", True, 2)
83
122
 
84
123
  key_exists = self.ssh_key_file.exists() and (self.ssh_key_file.with_suffix('.pub')).exists()
85
124
 
86
125
  if key_exists:
87
- print(f"SSH key '{self.ssh_key}' already exists.")
126
+ self.print_status(f"SSH key '{self.ssh_key}' exists", True, 2)
88
127
  self.using_1password = input("Would you like to use 1Password SSH agent? (y/N): ").lower() == 'y'
89
- if not self.using_1password:
128
+ if self.using_1password:
129
+ self.print_status("Using 1Password SSH agent", True, 2)
130
+ else:
90
131
  store_in_1password = input("Would you like to store the private key in 1Password? (y/N): ").lower() == 'y'
91
132
  if store_in_1password:
92
- self._store_key_in_1password()
133
+ if self._store_key_in_1password():
134
+ self.print_status("Private key stored in 1Password", True, 2)
135
+ else:
136
+ self.print_status("Failed to store private key in 1Password", False, 2)
93
137
  else:
94
- print(f"Generating new SSH key '{self.ssh_key}'...")
138
+ self.print_status(f"Generating new SSH key '{self.ssh_key}'...", None, 2)
95
139
  subprocess.run([
96
140
  'ssh-keygen',
97
141
  '-t', 'ed25519',
98
142
  '-f', str(self.ssh_key_file),
99
143
  '-N', '' # Empty passphrase
100
144
  ], check=True)
145
+ self.print_status("SSH key generated", True, 2)
101
146
 
102
147
  self.using_1password = input("Would you like to use 1Password SSH agent? (y/N): ").lower() == 'y'
103
- if not self.using_1password:
148
+ if self.using_1password:
149
+ self.print_status("Using 1Password SSH agent", True, 2)
150
+ else:
104
151
  store_in_1password = input("Would you like to store the private key in 1Password? (y/N): ").lower() == 'y'
105
152
  if store_in_1password:
106
- self._store_key_in_1password()
153
+ if self._store_key_in_1password():
154
+ self.print_status("Private key stored in 1Password", True, 2)
155
+ else:
156
+ self.print_status("Failed to store private key in 1Password", False, 2)
107
157
 
108
158
  return True
109
159
 
110
160
  except Exception as e:
111
- print(f"Error setting up SSH key: {e}")
161
+ self.print_status(f"Error: {str(e)}", False, 2)
162
+ continue_setup = input("Would you like to continue anyway? (Y/n): ").lower() != 'n'
163
+ if continue_setup:
164
+ self.print_status("Continuing setup despite SSH key issues", None, 2)
165
+ return True
112
166
  return False
113
167
 
114
168
  def _store_key_in_1password(self) -> bool:
@@ -167,6 +221,8 @@ class CloudXSetup:
167
221
  Returns:
168
222
  bool: True if config was set up successfully
169
223
  """
224
+ self.print_status("Setting up SSH configuration...")
225
+
170
226
  try:
171
227
  # Check if we need to create base config
172
228
  need_base_config = True
@@ -175,8 +231,10 @@ class CloudXSetup:
175
231
  # Check if configuration for this environment already exists
176
232
  if f"Host cloudx-{cloudx_env}-*" in current_config:
177
233
  need_base_config = False
234
+ self.print_status(f"Found existing config for cloudx-{cloudx_env}-*", True, 2)
178
235
 
179
236
  if need_base_config:
237
+ self.print_status(f"Creating new config for cloudx-{cloudx_env}-*", None, 2)
180
238
  # Build ProxyCommand with all necessary parameters
181
239
  proxy_command = f"uvx cloudx-proxy connect %h %p --profile {self.profile}"
182
240
  if self.aws_env:
@@ -208,14 +266,17 @@ Host cloudx-{cloudx_env}-*
208
266
  f.write("\n" + base_config)
209
267
  else:
210
268
  self.ssh_config_file.write_text(base_config)
269
+ self.print_status("Base configuration created", True, 2)
211
270
 
212
271
  # Add specific host entry
272
+ self.print_status(f"Adding host entry for cloudx-{cloudx_env}-{hostname}", None, 2)
213
273
  host_entry = f"""
214
274
  Host cloudx-{cloudx_env}-{hostname}
215
275
  HostName {instance_id}
216
276
  """
217
277
  with open(self.ssh_config_file, 'a') as f:
218
278
  f.write(host_entry)
279
+ self.print_status("Host entry added", True, 2)
219
280
 
220
281
  # Ensure main SSH config includes our config
221
282
  main_config = Path(self.home_dir) / ".ssh" / "config"
@@ -226,18 +287,26 @@ Host cloudx-{cloudx_env}-{hostname}
226
287
  if include_line not in content:
227
288
  with open(main_config, 'a') as f:
228
289
  f.write(f"\n{include_line}")
290
+ self.print_status("Added include line to main SSH config", True, 2)
291
+ else:
292
+ self.print_status("Main SSH config already includes our config", True, 2)
229
293
  else:
230
294
  main_config.write_text(include_line)
295
+ self.print_status("Created main SSH config with include line", True, 2)
231
296
 
232
- print(f"\nSSH configuration has been set up:")
233
- print(f"- Main config file: {main_config}")
234
- print(f"- CloudX config file: {self.ssh_config_file}")
235
- print(f"\nYou can now connect using: ssh cloudx-{cloudx_env}-{hostname}")
297
+ self.print_status("\nSSH configuration summary:", None)
298
+ self.print_status(f"Main config: {main_config}", None, 2)
299
+ self.print_status(f"CloudX config: {self.ssh_config_file}", None, 2)
300
+ self.print_status(f"Connect using: ssh cloudx-{cloudx_env}-{hostname}", None, 2)
236
301
 
237
302
  return True
238
303
 
239
304
  except Exception as e:
240
- print(f"Error setting up SSH config: {e}")
305
+ self.print_status(f"Error: {str(e)}", False, 2)
306
+ continue_setup = input("Would you like to continue anyway? (Y/n): ").lower() != 'n'
307
+ if continue_setup:
308
+ self.print_status("Continuing setup despite SSH config issues", None, 2)
309
+ return True
241
310
  return False
242
311
 
243
312
  def check_instance_setup(self, instance_id: str) -> Tuple[bool, bool]:
@@ -304,33 +373,44 @@ Host cloudx-{cloudx_env}-{hostname}
304
373
  Returns:
305
374
  bool: True if setup completed successfully
306
375
  """
307
- print(f"Checking setup status for instance {instance_id}...")
376
+ self.print_status(f"Checking instance {instance_id} setup status...")
308
377
 
309
378
  is_running, is_complete = self.check_instance_setup(instance_id)
310
379
 
311
380
  if not is_running:
312
- print("Error: Instance is not running or not accessible via SSM")
381
+ self.print_status("Instance is not running or not accessible via SSM", False, 2)
382
+ continue_setup = input("Would you like to continue anyway? (Y/n): ").lower() != 'n'
383
+ if continue_setup:
384
+ self.print_status("Continuing setup despite instance access issues", None, 2)
385
+ return True
313
386
  return False
314
387
 
315
388
  if is_complete:
316
- print("Instance setup is already complete")
389
+ self.print_status("Instance setup is complete", True, 2)
317
390
  return True
318
391
 
319
392
  wait = input("Instance setup is not complete. Would you like to wait? (Y/n): ").lower() != 'n'
320
393
  if not wait:
321
- return False
394
+ self.print_status("Skipping instance setup check", None, 2)
395
+ return True
322
396
 
323
- print("Waiting for setup to complete", end='', flush=True)
397
+ self.print_status("Waiting for setup to complete...", None, 2)
398
+ dots = 0
324
399
  while True:
325
400
  is_running, is_complete = self.check_instance_setup(instance_id)
326
401
 
327
402
  if not is_running:
328
- print("\nError: Instance is no longer running or accessible")
403
+ self.print_status("Instance is no longer running or accessible", False, 2)
404
+ continue_setup = input("Would you like to continue anyway? (Y/n): ").lower() != 'n'
405
+ if continue_setup:
406
+ self.print_status("Continuing setup despite instance issues", None, 2)
407
+ return True
329
408
  return False
330
409
 
331
410
  if is_complete:
332
- print("\nInstance setup completed successfully")
411
+ self.print_status("Instance setup completed successfully", True, 2)
333
412
  return True
334
413
 
335
- print(".", end='', flush=True)
414
+ dots = (dots + 1) % 4
415
+ print(f"\r {'.' * dots}{' ' * (3 - dots)}", end='', flush=True)
336
416
  time.sleep(10)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: cloudx-proxy
3
- Version: 0.1.1
3
+ Version: 0.2.0
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes