snapctl 0.48.1__py3-none-any.whl → 0.49.0__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.

Potentially problematic release.


This version of snapctl might be problematic. Click here for more details.

@@ -11,10 +11,10 @@ import subprocess
11
11
  import platform as sys_platform
12
12
  from sys import platform
13
13
  from typing import Union, List
14
+ import importlib.resources as pkg_resources
14
15
  import requests
15
16
  from requests.exceptions import RequestException
16
17
  import yaml
17
-
18
18
  from rich.progress import Progress, SpinnerColumn, TextColumn
19
19
  from snapctl.commands.snapend import Snapend
20
20
  from snapctl.config.constants import SERVER_CALL_TIMEOUT
@@ -29,10 +29,11 @@ from snapctl.config.constants import HTTP_ERROR_SERVICE_VERSION_EXISTS, \
29
29
  SNAPCTL_BYOSNAP_UPDATE_VERSION_ERROR, SNAPCTL_BYOSNAP_UPDATE_VERSION_SERVICE_IN_USE_ERROR, \
30
30
  SNAPCTL_BYOSNAP_UPDATE_VERSION_TAG_ERROR, SNAPCTL_BYOSNAP_NOT_FOUND, \
31
31
  HTTP_ERROR_RESOURCE_NOT_FOUND, SNAPCTL_BYOSNAP_PUBLISH_ERROR, \
32
- SNAPCTL_BYOSNAP_GENERATE_PROFILE_ERROR, SNAPCTL_CONFIGURATION_INCORRECT, SNAPCTL_BYOSNAP_SWAGGER_ERROR
32
+ SNAPCTL_BYOSNAP_GENERATE_PROFILE_ERROR, SNAPCTL_CONFIGURATION_INCORRECT
33
33
  from snapctl.utils.echo import info, warning, success
34
34
  from snapctl.utils.helper import get_composite_token, snapctl_error, snapctl_success, \
35
35
  check_dockerfile_architecture, check_use_containerd_snapshotter
36
+ import snapctl.data.profiles
36
37
 
37
38
 
38
39
  class ByoSnap:
@@ -52,8 +53,8 @@ class ByoSnap:
52
53
  TO_DEPRECATE_SUBCOMMANDS = [
53
54
  'create', 'publish-image', 'publish-version', 'update-version']
54
55
  DEFAULT_PROFILE_NAME_JSON = 'snapser-byosnap-profile.json'
56
+ DEFAULT_PROFILE_NAME_YML = 'snapser-byosnap-profile.yml'
55
57
  DEFAULT_PROFILE_NAME_YAML = 'snapser-byosnap-profile.yaml'
56
- PROFILE_FILES_PATH = 'snapctl/data/profiles/'
57
58
  PROFILE_FORMATS = ['json', 'yaml', 'yml']
58
59
  PLATFORMS = ['linux/arm64', 'linux/amd64']
59
60
  LANGUAGES = ['go', 'node', 'python', 'java', 'csharp', 'cpp', 'rust',
@@ -501,13 +502,23 @@ class ByoSnap:
501
502
  )
502
503
 
503
504
  @staticmethod
504
- def _handle_output_file(input_filepath, output_filepath) -> bool:
505
- file_written = False
506
- with open(input_filepath, 'r') as in_file, open(output_filepath, 'w') as outfile:
507
- for line in in_file:
508
- outfile.write(line)
509
- file_written = True
510
- return file_written
505
+ def _handle_output_file(resource_filename: str, output_filepath: str) -> bool:
506
+ try:
507
+ with pkg_resources.open_text(snapctl.data.profiles, resource_filename) as in_file, open(output_filepath, 'w') as outfile:
508
+ for line in in_file:
509
+ outfile.write(line)
510
+ return True
511
+ except FileNotFoundError:
512
+ print(f"[ERROR] Could not find profile file: {resource_filename}")
513
+ return False
514
+ # @staticmethod
515
+ # def _handle_output_file(input_filepath, output_filepath) -> bool:
516
+ # file_written = False
517
+ # with open(input_filepath, 'r') as in_file, open(output_filepath, 'w') as outfile:
518
+ # for line in in_file:
519
+ # outfile.write(line)
520
+ # file_written = True
521
+ # return file_written
511
522
 
512
523
  def _get_profile_contents(self) -> dict:
513
524
  """
@@ -1574,9 +1585,9 @@ class ByoSnap:
1574
1585
  file_save_path = os.path.join(
1575
1586
  os.getcwd(), self.profile_filename)
1576
1587
  extension = self.profile_filename.split('.')[-1]
1588
+ resource_filename = f"snapser-byosnap-profile.{extension}"
1577
1589
  file_written = ByoSnap._handle_output_file(
1578
- f"{ByoSnap.PROFILE_FILES_PATH}snapser-byosnap-profile.{extension}", file_save_path
1579
- )
1590
+ resource_filename, file_save_path)
1580
1591
  if file_written:
1581
1592
  snapctl_success(
1582
1593
  message="BYOSNAP Profile generation successful. " +
@@ -0,0 +1,421 @@
1
+ """
2
+ BYOWs CLI commands
3
+ """
4
+ import os
5
+ import platform
6
+ import subprocess
7
+ import shutil
8
+ import time
9
+ import json
10
+ from pathlib import Path
11
+ from typing import Union
12
+ import threading
13
+ import requests
14
+ from requests.exceptions import RequestException, HTTPError
15
+ from rich.progress import Progress, SpinnerColumn, TextColumn
16
+ from snapctl.config.constants import SERVER_CALL_TIMEOUT, SNAPCTL_INPUT_ERROR, \
17
+ SNAPCTL_BYOWS_GENERIC_ERROR, SNAPCTL_DEPENDENCY_MISSING
18
+ from snapctl.utils.helper import snapctl_error, snapctl_success, get_dot_snapser_dir
19
+ from snapctl.utils.echo import info, warning
20
+
21
+ class Byows:
22
+ """
23
+ CLI commands exposed for a Bring your own Workstation
24
+ """
25
+ SUBCOMMANDS = ['attach']
26
+ SSH_FILE = 'id_snapser_byows_attach_ed25519'
27
+ PORT_FORWARD_TTL = 3600 * 24 * 7
28
+ BYOWS_ENV_FILE = 'byows_env_setup'
29
+
30
+ def __init__(
31
+ self, *, subcommand: str, base_url: str, base_snapend_url: str, api_key: Union[str, None],
32
+ snapend_id: Union[str, None] = None, byosnap_id: Union[str, None] = None,
33
+ http_port: Union[int, None] = None, grpc_port: Union[int, None] = None,
34
+
35
+ ) -> None:
36
+ self.subcommand: str = subcommand
37
+ self.base_url: str = base_url
38
+ self.base_snapend_url: str = base_snapend_url
39
+ self.api_key: Union[str, None] = api_key
40
+ self.snapend_id: Union[str, None] = snapend_id
41
+ self.byosnap_id: Union[str, None] = byosnap_id
42
+ self.http_port: Union[int, None] = http_port
43
+ self.grpc_port: Union[int, None] = grpc_port
44
+ self.validate_input()
45
+
46
+ def validate_input(self) -> None:
47
+ """
48
+ Validator
49
+ """
50
+ # Check API Key and Base URL
51
+ if not self.api_key or self.base_url == '':
52
+ snapctl_error(
53
+ message="Missing API Key.", code=SNAPCTL_INPUT_ERROR)
54
+ # Check subcommand
55
+ if not self.subcommand in Byows.SUBCOMMANDS:
56
+ snapctl_error(
57
+ message="Invalid command. Valid commands are " +
58
+ f"{', '.join(Byows.SUBCOMMANDS)}.",
59
+ code=SNAPCTL_INPUT_ERROR)
60
+ # Check sdk-download commands
61
+ if self.subcommand == 'attach':
62
+ if not shutil.which("ssh"):
63
+ snapctl_error("ssh is not installed or not in PATH",
64
+ SNAPCTL_DEPENDENCY_MISSING)
65
+ if self.snapend_id is None or self.snapend_id == '':
66
+ snapctl_error(
67
+ message="Missing Input --snapend-id=$your_snapend_id",
68
+ code=SNAPCTL_INPUT_ERROR)
69
+ if self.byosnap_id is None or self.byosnap_id == '':
70
+ snapctl_error(
71
+ message="Missing Input --byosnap-id=$your_byosnap_id",
72
+ code=SNAPCTL_INPUT_ERROR)
73
+ if self.http_port is None and self.grpc_port is None:
74
+ snapctl_error(
75
+ message="Missing Input. One of --http-port=$your_local_server_port or --grpc-port=$your_local_server_port is required.",
76
+ code=SNAPCTL_INPUT_ERROR)
77
+
78
+
79
+ # Static methods
80
+ @staticmethod
81
+ def stream_output(pipe):
82
+ '''
83
+ Stream the output of a subprocess to the console.
84
+ This function reads lines from the pipe and prints them to the console.
85
+ '''
86
+ for line in iter(pipe.readline, b''):
87
+ info(line.decode().rstrip())
88
+
89
+ @staticmethod
90
+ def _write_export_command_to_console(snap_ids, port):
91
+ '''
92
+ TODO: This is not used anymore
93
+ Generate export commands for the given snap IDs and port.
94
+ '''
95
+ env_vars = []
96
+
97
+ for snap_id in snap_ids:
98
+ upper_id = snap_id.upper()
99
+ env_vars.append(f"SNAPEND_{upper_id}_GRPC_URL=localhost:{port}")
100
+ env_vars.append(
101
+ f"SNAPEND_{upper_id}_HTTP_URL=http://localhost:{port}")
102
+
103
+ system = platform.system()
104
+
105
+ if system == "Windows":
106
+ # PowerShell syntax
107
+ return "; ".join([f'$env:{env_vars[i].replace("=", " = ")}; $env:{env_vars[i+1].replace("=", " = ")}'
108
+ for i in range(0, len(env_vars), 2)])
109
+ else:
110
+ # Linux or macOS bash/zsh syntax
111
+ return "export \\\n" + " \\\n".join([f" {env_vars[i]} {env_vars[i+1]}"
112
+ for i in range(0, len(env_vars), 2)])
113
+
114
+ @staticmethod
115
+ def _generate_env_file(snap_ids, port):
116
+ '''
117
+ Generate an environment file with the given snap IDs and port.
118
+ '''
119
+ env_lines = []
120
+
121
+ for snap_id in snap_ids:
122
+ upper_id = snap_id.upper()
123
+ env_lines.append(
124
+ f"export SNAPEND_{upper_id}_GRPC_URL=localhost:{port}")
125
+ env_lines.append(
126
+ f"export SNAPEND_{upper_id}_HTTP_URL=http://localhost:{port}")
127
+
128
+ system = platform.system()
129
+ filename = f"{Byows.BYOWS_ENV_FILE}.ps1" if system == "Windows" else \
130
+ f"{Byows.BYOWS_ENV_FILE}.sh"
131
+ env_file = get_dot_snapser_dir() / filename
132
+
133
+ with env_file.open("w") as f:
134
+ if system == "Windows":
135
+ for line in env_lines:
136
+ var, value = line.replace("export ", "").split("=", 1)
137
+ f.write(f'$env:{var} = "{value}"\n')
138
+ else:
139
+ for line in env_lines:
140
+ f.write(f"{line}\n")
141
+
142
+ return env_file
143
+
144
+ @staticmethod
145
+ def _generate_env(snap_ids, port):
146
+ '''
147
+ Generate environment variables for the given snap IDs and port.
148
+ '''
149
+ env_file = Byows._generate_env_file(snap_ids, port)
150
+ info(f"Environment variables written to {env_file}")
151
+ system = platform.system()
152
+ if system == "Windows":
153
+ info(
154
+ f"Run the following command to set them in your session:\n\n . {env_file}\n")
155
+ else:
156
+ info(
157
+ f"Run the following command to set them in your shell:\n\n source {env_file}\n")
158
+
159
+ @staticmethod
160
+ def _format_portal_http_error(msg, http_err, response):
161
+ """
162
+ Format a portal HTTP error response for display.
163
+ Args:
164
+ http_err: The HTTPError exception.
165
+ response: The HTTP response object.
166
+ Returns:
167
+ str: A nicely formatted error message.
168
+ """
169
+ try:
170
+ # Check if the response content is JSON-like
171
+ # FIXME: The portal should always return application/json for errors, but doesn't.
172
+ if response.text.strip().startswith('{'):
173
+ error_data = response.json()
174
+ api_error_code = error_data.get(
175
+ "api_error_code", "Unknown Code")
176
+ message = error_data.get("message", "No message provided")
177
+ details = error_data.get("details", [])
178
+ if details:
179
+ details_str = "\n - " + "\n - ".join(details)
180
+ else:
181
+ details_str = ""
182
+ return (
183
+ f"Message: {msg}\n"
184
+ f"Exception: {http_err}\n"
185
+ f"Snapser Error Code: {api_error_code}\n"
186
+ f"Error: {message}{details_str}"
187
+ )
188
+ except Exception:
189
+ pass # Fallback to default error formatting if parsing fails
190
+
191
+ # Default error message if not JSON or parsing fails
192
+ return f"HTTP Error {http_err.response.status_code}: {http_err.response.reason}\nResponse: {response.text.strip()}"
193
+
194
+ # Private methods
195
+ def _setup_port_forward(
196
+ self, private_key, public_key, snapend_id, snap_id, port, reverse_port, reverse_grpc_port,
197
+ incoming_http, incoming_grpc, outgoing_http, snap_ids, ssh_connect_addr) -> bool:
198
+ '''
199
+ Setup the SSH port forward
200
+ '''
201
+ dot_snapser_dir = get_dot_snapser_dir()
202
+ id_file = dot_snapser_dir / Byows.SSH_FILE
203
+ pub_file = dot_snapser_dir / f'{Byows.SSH_FILE}.pub'
204
+
205
+ # Write public and private key files
206
+ with open(id_file, 'w') as f:
207
+ f.write(private_key)
208
+ if os.name != 'nt':
209
+ # Only chmod on Unix-like systems
210
+ os.chmod(id_file, 0o600)
211
+
212
+ # write the public key
213
+ with open(pub_file, 'w') as f:
214
+ f.write(public_key)
215
+
216
+ # Let user know about the forwarding
217
+ # TODO: AJ, needs to be the proper base url
218
+ info(f"Forwarding {self.base_snapend_url}/{snapend_id}/v1/{snap_id}" +
219
+ f"-> http://localhost:{incoming_http}/*")
220
+ info(
221
+ f"Your BYOSnap HTTP server should listen on: localhost:{incoming_http}")
222
+ info(
223
+ f"Connect to other snaps over HTTP on: localhost:{outgoing_http}")
224
+ Byows._generate_env(snap_ids, port)
225
+ info('Press <ctrl-c> to stop forwarding')
226
+
227
+ # TODO: ??? AJ get the ssh host for the current environ from BYOWS_SSH_HOSTS contact in endpoints.py
228
+ ssh_addr = ssh_connect_addr
229
+ if not ssh_addr:
230
+ snapctl_error(message=f"{env} is missing or empty in BYOWS_SSH_HOSTS", code=SNAPCTL_INPUT_ERROR)
231
+
232
+ # Extract the port from the ssh_addr if present, otherwise default to 22
233
+ if ':' in ssh_addr:
234
+ ssh_host, ssh_port = ssh_addr.split(':')
235
+ ssh_port = int(ssh_port)
236
+ else:
237
+ ssh_host = ssh_addr
238
+ ssh_port = 22
239
+
240
+ ssh_command = [
241
+ 'ssh',
242
+ '-o', 'ServerAliveInterval=60',
243
+ '-o', 'StrictHostKeyChecking=no',
244
+ '-o', 'UserKnownHostsFile=/dev/null',
245
+ '-p', str(ssh_port),
246
+ '-i', str(id_file),
247
+ '-N',
248
+ '-L', f'{outgoing_http}:localhost:{port}',
249
+ '-R', f'{reverse_port}:localhost:{incoming_http}',
250
+ ]
251
+ if incoming_grpc:
252
+ ssh_command += ['-R', f'{reverse_grpc_port}:localhost:{incoming_grpc}']
253
+ ssh_command += [ssh_host]
254
+
255
+ # print(f'Running SSH command: {ssh_command}')
256
+ process = None
257
+ try:
258
+ process = subprocess.Popen(
259
+ ssh_command,
260
+ shell=False,
261
+ stdout=subprocess.PIPE,
262
+ stderr=subprocess.PIPE
263
+ )
264
+
265
+ # Give it a few seconds to report any immediate errors
266
+ timeout_seconds = 5
267
+ start_time = time.time()
268
+ while True:
269
+ if process.poll() is not None:
270
+ # Process exited early — likely an error
271
+ stderr_output = process.stderr.read().decode().strip()
272
+ if stderr_output:
273
+ info(f"[SSH Error] {stderr_output}")
274
+ else:
275
+ warning("SSH process exited unexpectedly with no output.")
276
+ return False
277
+
278
+ if time.time() - start_time > timeout_seconds:
279
+ break
280
+
281
+ time.sleep(0.2)
282
+
283
+ # Start background thread to stream live stderr
284
+ threading.Thread(target=Byows.stream_output, args=(
285
+ process.stderr,), daemon=True).start()
286
+
287
+ # Now block for the full tunnel lifetime
288
+ process.wait(timeout=Byows.PORT_FORWARD_TTL)
289
+ except KeyboardInterrupt:
290
+ process.terminate()
291
+ try:
292
+ process.wait(timeout=5)
293
+ except subprocess.TimeoutExpired:
294
+ warning('SSH process did not shut down gracefully.')
295
+
296
+ info('Shutting down port forward...')
297
+
298
+ try:
299
+ url = f"{self.base_url}/v1/snapser-api/byows/snapends/" + \
300
+ f"{self.snapend_id}/snaps/{self.byosnap_id}/enabled"
301
+ res = requests.put(
302
+ url, headers={'api-key': self.api_key}, json=False, timeout=SERVER_CALL_TIMEOUT)
303
+ res.raise_for_status()
304
+ info('Forwarding disabled')
305
+ except HTTPError as http_err:
306
+ snapctl_error(
307
+ message=Byows._format_portal_http_error(
308
+ "Unable to disable BYOWs", http_err, res),
309
+ code=SNAPCTL_BYOWS_GENERIC_ERROR
310
+ )
311
+ return False
312
+ except RequestException as req_err:
313
+ warning(
314
+ f"Response Status Code: {res.status_code}, Body: {res.text}")
315
+ snapctl_error(
316
+ message=f"Request Exception: Unable to disable BYOWs {req_err}",
317
+ code=SNAPCTL_BYOWS_GENERIC_ERROR
318
+ )
319
+ return False
320
+ except Exception as e:
321
+ snapctl_error(
322
+ message=f"Unexpected error occurred: {e}",
323
+ code=SNAPCTL_BYOWS_GENERIC_ERROR
324
+ )
325
+ return False
326
+
327
+ except Exception as e:
328
+ print('Error running SSH command:', e)
329
+ return False
330
+ finally:
331
+ # Cleanup the SSH key files
332
+ # Only remove the files if they exist
333
+ try:
334
+ id_file.unlink()
335
+ pub_file.unlink()
336
+ except Exception as e:
337
+ warning(
338
+ f"Cleanup warning: Failed to delete SSH key files – {e}")
339
+ return True
340
+
341
+ # Commands
342
+ def attach(self) -> bool:
343
+ """
344
+ BYOWs port forward
345
+ """
346
+ progress = Progress(
347
+ SpinnerColumn(),
348
+ TextColumn("[progress.description]{task.description}"),
349
+ transient=True,
350
+ )
351
+ progress.start()
352
+ progress.add_task(
353
+ description='Setting up BYOWs port forward...', total=None)
354
+ try:
355
+ url = f"{self.base_url}/v1/snapser-api/byows/snapends/" + \
356
+ f"{self.snapend_id}/snaps/{self.byosnap_id}"
357
+ res = requests.put(
358
+ url,
359
+ headers={'api-key': self.api_key},
360
+ json={
361
+ "snapend_id": self.snapend_id,
362
+ "snap_id": self.byosnap_id
363
+ },
364
+ timeout=SERVER_CALL_TIMEOUT
365
+ )
366
+ res.raise_for_status()
367
+ response_json = res.json()
368
+
369
+ # For debugging
370
+ #print(f"BYOWS Response:\n{json.dumps(response_json, indent=2)}")
371
+
372
+ if res.ok and 'workstationPort' in response_json and \
373
+ 'workstationReversePort' in response_json and \
374
+ 'snapendId' in response_json and \
375
+ 'proxyPrivateKey' in response_json and \
376
+ 'proxyPublicKey' in response_json:
377
+
378
+ # We do not use the workstationReversePort now, we ask the user
379
+ # to provide the port they want to use
380
+ # incoming_http_port = response_json['workstationReversePort']
381
+ incoming_http_port = self.http_port
382
+ incoming_grpc_port = self.grpc_port
383
+
384
+ outgoing_http_port = response_json['workstationPort']
385
+
386
+ self._setup_port_forward(
387
+ response_json['proxyPrivateKey'],
388
+ response_json['proxyPublicKey'],
389
+ response_json['snapendId'],
390
+ response_json['snapId'],
391
+ response_json['workstationPort'],
392
+ response_json['workstationReversePort'],
393
+ response_json['workstationReverseGrpcPort'],
394
+ incoming_http_port,
395
+ incoming_grpc_port,
396
+ outgoing_http_port,
397
+ response_json['snapIds'],
398
+ response_json['sshConnectAddr'],
399
+ )
400
+ return snapctl_success(
401
+ message='complete', progress=progress)
402
+ snapctl_error(
403
+ message='Unable to setup port forward.',
404
+ code=SNAPCTL_BYOWS_GENERIC_ERROR, progress=progress)
405
+ except HTTPError as http_err:
406
+ snapctl_error(
407
+ message=Byows._format_portal_http_error(
408
+ "Unable to setup port forward", http_err, res),
409
+ code=SNAPCTL_BYOWS_GENERIC_ERROR, progress=progress)
410
+ snapctl_error(
411
+ message=f"Server Error: Unable to setup port forward {http_err}",
412
+ code=SNAPCTL_BYOWS_GENERIC_ERROR, progress=progress)
413
+ except RequestException as e:
414
+ snapctl_error(
415
+ message=f"Exception: Unable to setup port forward {e}",
416
+ code=SNAPCTL_BYOWS_GENERIC_ERROR, progress=progress)
417
+ finally:
418
+ progress.stop()
419
+ snapctl_error(
420
+ message='Failed to setup port forward.',
421
+ code=SNAPCTL_BYOWS_GENERIC_ERROR, progress=progress)
snapctl/commands/game.py CHANGED
@@ -40,7 +40,7 @@ class Game:
40
40
  # Check subcommand
41
41
  if not self.subcommand in Game.SUBCOMMANDS:
42
42
  snapctl_error(
43
- message="Invalid command. Valid commands are" +
43
+ message="Invalid command. Valid commands are " +
44
44
  f"{', '.join(Game.SUBCOMMANDS)}.",
45
45
  code=SNAPCTL_INPUT_ERROR)
46
46
  # Check sdk-download commands
@@ -1,8 +1,9 @@
1
1
  """
2
- Release Notes Command
2
+ Release Notes
3
3
  """
4
- import os
5
4
  from typing import Union
5
+ import importlib.resources as pkg_resources
6
+ import snapctl.data.releases # must have __init__.py under releases
6
7
  from snapctl.config.constants import SNAPCTL_INPUT_ERROR
7
8
  from snapctl.utils.helper import snapctl_error, snapctl_success
8
9
 
@@ -12,7 +13,6 @@ class ReleaseNotes:
12
13
  Release Notes Command
13
14
  """
14
15
  SUBCOMMANDS = ["releases", "show"]
15
- RELEASES_PATH = 'snapctl/data/releases'
16
16
 
17
17
  def __init__(self, *, subcommand: str, version: Union[str, None] = None) -> None:
18
18
  self.subcommand = subcommand
@@ -29,19 +29,15 @@ class ReleaseNotes:
29
29
  f"{', '.join(ReleaseNotes.SUBCOMMANDS)}.",
30
30
  code=SNAPCTL_INPUT_ERROR)
31
31
 
32
- # Upper echelon commands
33
32
  def releases(self) -> None:
34
33
  """
35
34
  List versions
36
35
  """
37
- # List all files and directories in the specified path
38
- files_and_directories = os.listdir(ReleaseNotes.RELEASES_PATH)
39
-
40
- # Print only files, excluding subdirectories
41
36
  print('== Releases ' + '=' * (92))
42
- for item in files_and_directories:
43
- if os.path.isfile(os.path.join(ReleaseNotes.RELEASES_PATH, item)):
44
- print(item.replace('.mdx', '').replace('.md', ''))
37
+ # List all resource files in snapctl.data.releases
38
+ for resource in pkg_resources.contents(snapctl.data.releases):
39
+ if resource.endswith('.mdx'):
40
+ print(resource.replace('.mdx', '').replace('.md', ''))
45
41
  print('=' * (104))
46
42
  snapctl_success(message="List versions")
47
43
 
@@ -49,17 +45,15 @@ class ReleaseNotes:
49
45
  """
50
46
  Show version
51
47
  """
52
- # Check if the specified version exists
53
- version_file = os.path.join(
54
- ReleaseNotes.RELEASES_PATH, f'{self.version}.mdx')
55
- if not os.path.isfile(version_file):
48
+ version_filename = f"{self.version}.mdx"
49
+
50
+ if version_filename not in pkg_resources.contents(snapctl.data.releases):
56
51
  snapctl_error(
57
52
  message=f"Version {self.version} does not exist.",
58
53
  code=SNAPCTL_INPUT_ERROR)
59
54
 
60
- # Read the contents of the specified version file
61
- print('== Releases Notes ' + '=' * (86))
62
- with open(version_file, 'r') as file:
55
+ print('== Release Notes ' + '=' * (86))
56
+ with pkg_resources.open_text(snapctl.data.releases, version_filename) as file:
63
57
  print(file.read())
64
58
  print('=' * (104))
65
59
  snapctl_success(message=f"Show version {self.version}")
@@ -37,7 +37,9 @@ class Snapend:
37
37
  DOWNLOAD_CATEGORY = [
38
38
  'sdk', 'protos', 'snapend-manifest'
39
39
  ]
40
- CATEGORY_TYPE_SDK = ['user', 'server', 'internal', 'app']
40
+ CATEGORY_TYPE_SDK = [
41
+ # 'omni',
42
+ 'user', 'api-key', 'internal', 'app']
41
43
  CATEGORY_TYPE_PROTOS = ['messages', 'services']
42
44
  # CATEGORY_TYPE_HTTP_LIB_FORMATS = ['unity', 'web-ts', 'ts']
43
45
 
@@ -412,26 +414,26 @@ class Snapend:
412
414
  )
413
415
  # Special cases for client SDKs
414
416
  if self.category_format in CLIENT_SDK_TYPES:
415
- if self.category_type in ['server', 'app']:
417
+ if self.category_type in ['api-key', 'app', 'internal']:
416
418
  snapctl_error(
417
419
  message="Invalid combination of format and type. " +
418
420
  ", ".join(CLIENT_SDK_TYPES.keys()) +
421
+ # " SDKs are only available for --type=omni or --type=user",
419
422
  " SDKs are only available for --type=user",
420
423
  code=SNAPCTL_INPUT_ERROR
421
424
  )
422
- if self.category_type == 'internal':
423
- snapctl_error(
424
- message="Internal access type is not supported for " +
425
- ", ".join(CLIENT_SDK_TYPES.keys()) + " SDKs.",
426
- code=SNAPCTL_INPUT_ERROR
427
- )
425
+ # if self.category_type == 'internal':
426
+ # snapctl_error(
427
+ # message="Internal access type is not supported for " +
428
+ # ", ".join(CLIENT_SDK_TYPES.keys()) + " SDKs.",
429
+ # code=SNAPCTL_INPUT_ERROR
430
+ # )
428
431
  if self.category_http_lib:
429
432
  # First check if the format supports http-lib
430
433
  if self.category_format in Snapend.get_formats_supporting_http_lib():
431
434
  # Check if the http-lib is supported for the format
432
435
  valid_http_libs = SDK_TYPES[self.category_format]['http-lib']
433
436
  if self.category_http_lib not in valid_http_libs:
434
- print('Caught you')
435
437
  snapctl_error(
436
438
  message="Invalid HTTP Library. Valid libraries are " +
437
439
  Snapend.get_http_formats_str(),
@@ -3,7 +3,7 @@ Constants used by snapctl
3
3
  """
4
4
  COMPANY_NAME = 'Snapser'
5
5
  VERSION_PREFIX = 'beta-'
6
- VERSION = '0.48.1'
6
+ VERSION = '0.49.0'
7
7
  CONFIG_FILE_MAC = '~/.snapser/config'
8
8
  CONFIG_FILE_WIN = '%homepath%\\.snapser\\config'
9
9
 
@@ -50,7 +50,7 @@ SNAPCTL_BYOGS_PUBLISH_ERROR = 25
50
50
  SNAPCTL_BYOGS_PUBLISH_PERMISSION_ERROR = 26
51
51
  SNAPCTL_BYOGS_PUBLISH_DUPLICATE_TAG_ERROR = 27
52
52
 
53
- # BYOSNAP Errors - 30 - 49 then 86 - 99
53
+ # BYOSNAP Errors - 30 - 49 then 86 - 90
54
54
  SNAPCTL_BYOSNAP_NOT_FOUND = SNAPCTL_RESOURCE_NOT_FOUND
55
55
  SNAPCTL_BYOSNAP_GENERIC_ERROR = 30
56
56
  SNAPCTL_BYOSNAP_DEPENDENCY_MISSING = 31
@@ -108,3 +108,6 @@ SNAPCTL_SNAPEND_APPLY_MANIFEST_MISMATCH_ERROR = 76
108
108
  # Generate Errors - 80 - 85
109
109
  SNAPCTL_GENERATE_GENERIC_ERROR = 80
110
110
  SNAPCTL_GENERATE_CREDENTIALS_ERROR = 81
111
+
112
+ # BYOWs Errors - 95 - 99
113
+ SNAPCTL_BYOWS_GENERIC_ERROR = 95
@@ -9,3 +9,8 @@ END_POINTS: Dict[str, str] = {
9
9
  'PLAYTEST': 'https://gateway.dev.snapser.io/playtest',
10
10
  'PROD': 'https://gateway.snapser.com/snapser'
11
11
  }
12
+
13
+ GATEWAY_END_POINTS: Dict[str, str] = {
14
+ 'SANDBOX': 'https://gateway.dev.snapser.io',
15
+ 'LIVE': 'https://gateway-accel.snapser.com',
16
+ }
snapctl/config/hashes.py CHANGED
@@ -189,11 +189,15 @@ SERVICE_IDS: List[str] = [
189
189
  ]
190
190
 
191
191
  SDK_ACCESS_AUTH_TYPE_LOOKUP: Dict[str, Dict[str, str]] = {
192
+ # 'omni': {
193
+ # 'access_type': 'external',
194
+ # 'auth_type': 'omni'
195
+ # },
192
196
  'user': {
193
197
  'access_type': 'external',
194
198
  'auth_type': 'user'
195
199
  },
196
- 'server': {
200
+ 'api-key': {
197
201
  'access_type': 'external',
198
202
  'auth_type': 'api-key'
199
203
  },
File without changes
File without changes