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

snapctl/commands/byogs.py CHANGED
@@ -25,14 +25,19 @@ class ByoGs:
25
25
  BYOGS CLI commands
26
26
  """
27
27
  ID_PREFIX = 'byogs-'
28
- SUBCOMMANDS = ['create', 'publish-image', 'publish-version']
28
+ SUBCOMMANDS = [
29
+ 'build', 'push',
30
+ 'create', 'publish-image', 'publish-version',
31
+ ]
29
32
  PLATFORMS = ['linux/amd64']
30
33
  LANGUAGES = ['go', 'python', 'ruby', 'c#', 'c++', 'rust', 'java', 'node']
31
34
  DEFAULT_BUILD_PLATFORM = 'linux/amd64'
35
+ SID_CHARACTER_LIMIT = 47
36
+ TAG_CHARACTER_LIMIT = 80
32
37
 
33
38
  def __init__(
34
- self, subcommand: str, base_url: str, api_key: str, sid: str, name: str, desc: str,
35
- platform_type: str, language: str, tag: Union[str, None], path: Union[str, None],
39
+ self, subcommand: str, base_url: str, api_key: str | None, sid: str, name: str, desc: str,
40
+ platform_type: str, language: str, input_tag: Union[str, None], path: Union[str, None],
36
41
  dockerfile: str, version: Union[str, None], http_port: Union[int, None],
37
42
  debug_port: Union[int, None]
38
43
  ) -> None:
@@ -49,17 +54,19 @@ class ByoGs:
49
54
  base_url, api_key, 'byogs', {'service_id': sid})
50
55
  else:
51
56
  self.token: Union[str, None] = None
52
- self.token_parts: Union[list, None] = ByoGs.get_token_values(
57
+ self.token_parts: Union[list, None] = ByoGs._get_token_values(
53
58
  self.token) if self.token is not None else None
54
- self.tag: Union[str, None] = tag
59
+ self.input_tag: Union[str, None] = input_tag
55
60
  self.path: Union[str, None] = path
56
61
  self.dockerfile: str = dockerfile
57
62
  self.version: Union[str, None] = version
58
63
  self.http_port: Union[int, None] = http_port
59
64
  self.debug_port: Union[int, None] = debug_port
60
65
 
66
+ # Protected methods
67
+
61
68
  @staticmethod
62
- def get_token_values(token: str) -> None | list:
69
+ def _get_token_values(token: str) -> None | list:
63
70
  """
64
71
  Get the token values
65
72
  """
@@ -80,6 +87,168 @@ class ByoGs:
80
87
  pass
81
88
  return None
82
89
 
90
+ def _check_dependencies(self) -> bool:
91
+ try:
92
+ # Check dependencies
93
+ with Progress(
94
+ SpinnerColumn(),
95
+ TextColumn("[progress.description]{task.description}"),
96
+ transient=True,
97
+ ) as progress:
98
+ progress.add_task(
99
+ description='Checking dependencies...', total=None)
100
+ try:
101
+ subprocess.run([
102
+ "docker", "--version"
103
+ ], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT, check=False)
104
+ except subprocess.CalledProcessError:
105
+ error('Docker not present')
106
+ return False
107
+ success('Dependencies Verified')
108
+ return True
109
+ except subprocess.CalledProcessError:
110
+ error('CLI Error')
111
+ return False
112
+
113
+ def _docker_login(self) -> bool:
114
+ # Get the data
115
+ ecr_repo_url = self.token_parts[0]
116
+ ecr_repo_username = self.token_parts[1]
117
+ ecr_repo_token = self.token_parts[2]
118
+ try:
119
+ # Login to Snapser Registry
120
+ with Progress(
121
+ SpinnerColumn(),
122
+ TextColumn("[progress.description]{task.description}"),
123
+ transient=True,
124
+ ) as progress:
125
+ progress.add_task(
126
+ description='Logging into Snapser Image Registry...', total=None)
127
+ if platform == 'win32':
128
+ response = subprocess.run([
129
+ 'docker', 'login', '--username', ecr_repo_username,
130
+ '--password', ecr_repo_token, ecr_repo_url
131
+ ], shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT, check=False)
132
+ else:
133
+ response = subprocess.run([
134
+ f'echo "{ecr_repo_token}" | docker login '
135
+ f'--username {ecr_repo_username} --password-stdin {ecr_repo_url}'
136
+ ], shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT, check=False)
137
+ if response.returncode:
138
+ error(
139
+ 'Unable to connect to the Snapser Container Repository. '
140
+ 'Please confirm if docker is running or try restarting docker'
141
+ )
142
+ return False
143
+ success('Login Successful')
144
+ return True
145
+ except subprocess.CalledProcessError:
146
+ error('CLI Error')
147
+ return False
148
+
149
+ def _docker_build(self) -> bool:
150
+ # Get the data
151
+ image_tag = f'{self.sid}.{self.input_tag}'
152
+ build_platform = ByoGs.DEFAULT_BUILD_PLATFORM
153
+ if len(self.token_parts) == 4:
154
+ build_platform = self.token_parts[3]
155
+ try:
156
+ # Build your snap
157
+ with Progress(
158
+ SpinnerColumn(),
159
+ TextColumn("[progress.description]{task.description}"),
160
+ transient=True,
161
+ ) as progress:
162
+ progress.add_task(
163
+ description='Building your snap...', total=None)
164
+ if platform == "win32":
165
+ response = subprocess.run([
166
+ # f"docker build --no-cache -t {tag} {path}"
167
+ 'docker', 'build', '--platform', build_platform, '-t', image_tag, self.path
168
+ ], shell=True, check=False)
169
+ # stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
170
+ else:
171
+ response = subprocess.run([
172
+ # f"docker build --no-cache -t {tag} {path}"
173
+ f"docker build --platform {build_platform} -t {image_tag} {self.path}"
174
+ ], shell=True, check=False)
175
+ # stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
176
+ if response.returncode:
177
+ error('Unable to build docker')
178
+ return False
179
+ success('Build Successful')
180
+ return True
181
+ except subprocess.CalledProcessError:
182
+ error('CLI Error')
183
+ return False
184
+
185
+ def _docker_tag(self) -> bool:
186
+ # Get the data
187
+ ecr_repo_url = self.token_parts[0]
188
+ image_tag = f'{self.sid}.{self.input_tag}'
189
+ full_ecr_repo_url = f'{ecr_repo_url}:{image_tag}'
190
+ try:
191
+ # Tag the repo
192
+ with Progress(
193
+ SpinnerColumn(),
194
+ TextColumn("[progress.description]{task.description}"),
195
+ transient=True,
196
+ ) as progress:
197
+ progress.add_task(
198
+ description='Tagging your snap...', total=None)
199
+ if platform == "win32":
200
+ response = subprocess.run([
201
+ 'docker', 'tag', image_tag, full_ecr_repo_url
202
+ ], shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT, check=False)
203
+ else:
204
+ response = subprocess.run([
205
+ f"docker tag {image_tag} {full_ecr_repo_url}"
206
+ ], shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT, check=False)
207
+ if response.returncode:
208
+ error('Unable to tag your snap')
209
+ return False
210
+ success('Tag Successful')
211
+ return True
212
+ except subprocess.CalledProcessError:
213
+ error('CLI Error')
214
+ return False
215
+
216
+ def _docker_push(self) -> bool:
217
+ try:
218
+ ecr_repo_url = self.token_parts[0]
219
+ image_tag = f'{self.sid}.{self.input_tag}'
220
+ full_ecr_repo_url = f'{ecr_repo_url}:{image_tag}'
221
+
222
+ # Push the image
223
+ with Progress(
224
+ SpinnerColumn(),
225
+ TextColumn("[progress.description]{task.description}"),
226
+ transient=True,
227
+ ) as progress:
228
+ progress.add_task(
229
+ description='Pushing your snap...', total=None)
230
+ if platform == "win32":
231
+ response = subprocess.run([
232
+ 'docker', 'push', full_ecr_repo_url
233
+ ], shell=True, check=False)
234
+ # stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
235
+ else:
236
+ response = subprocess.run([
237
+ f"docker push {full_ecr_repo_url}"
238
+ ], shell=True, check=False)
239
+ # stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
240
+ if response.returncode:
241
+ error('Unable to push your snap')
242
+ return False
243
+ success('Snap Upload Successful')
244
+ return True
245
+ except subprocess.CalledProcessError:
246
+ error('CLI Error')
247
+ return False
248
+
249
+ # Public methods
250
+
251
+ # Validator
83
252
  def validate_input(self) -> ResponseType:
84
253
  """
85
254
  Validator
@@ -89,6 +258,10 @@ class ByoGs:
89
258
  'msg': '',
90
259
  'data': []
91
260
  }
261
+ # Check API Key and Base URL
262
+ if not self.api_key or self.base_url == '':
263
+ response['msg'] = "Missing API Key."
264
+ return response
92
265
  # Check subcommand
93
266
  if not self.subcommand in ByoGs.SUBCOMMANDS:
94
267
  response['msg'] = f"Invalid command. Valid commands are {', '.join(ByoGs.SUBCOMMANDS)}."
@@ -100,6 +273,12 @@ class ByoGs:
100
273
  f"with {ByoGs.ID_PREFIX}."
101
274
  )
102
275
  return response
276
+ if len(self.sid) > ByoGs.SID_CHARACTER_LIMIT:
277
+ response['msg'] = (
278
+ "Invalid Game Server ID. "
279
+ f"Game Server ID should be less than {ByoGs.SID_CHARACTER_LIMIT} characters"
280
+ )
281
+ return response
103
282
  # Validation for subcommands
104
283
  if self.subcommand == 'create':
105
284
  if self.name == '':
@@ -122,23 +301,34 @@ class ByoGs:
122
301
  response['msg'] = 'Invalid token. Please reach out to your support team'
123
302
  return response
124
303
  # Check tag
125
- if self.tag is None or len(self.tag.split()) > 1 or len(self.tag) > 25:
126
- response['msg'] = "Tag should be a single word with maximum of 25 characters"
304
+ if self.input_tag is None or len(self.input_tag.split()) > 1 or \
305
+ len(self.input_tag) > ByoGs.TAG_CHARACTER_LIMIT:
306
+ response['msg'] = (
307
+ "Tag should be a single word with maximum of "
308
+ f"{ByoGs.TAG_CHARACTER_LIMIT} characters"
309
+ )
127
310
  return response
128
- if self.subcommand == 'publish-image':
311
+ if self.subcommand == 'build' or self.subcommand == 'publish-image':
312
+ if not self.input_tag:
313
+ response['msg'] = "Missing required parameter: tag"
314
+ return response
129
315
  if not self.path:
130
- response['msg'] = "Missing path"
316
+ response['msg'] = "Missing required parameter: path"
131
317
  return response
132
318
  # Check path
133
319
  if not os.path.isfile(f"{self.path}/{self.dockerfile}"):
134
320
  response['msg'] = f"Unable to find {self.dockerfile} at path {self.path}"
135
321
  return response
322
+ elif self.subcommand == 'push':
323
+ if not self.input_tag:
324
+ response['msg'] = "Missing required parameter: tag"
325
+ return response
136
326
  elif self.subcommand == 'publish-version':
137
327
  if not self.version:
138
- response['msg'] = "Missing version"
328
+ response['msg'] = "Missing required parameter: version"
139
329
  return response
140
330
  if not self.http_port:
141
- response['msg'] = "Missing Ingress HTTP Port"
331
+ response['msg'] = "Missing required parameter: Ingress HTTP Port"
142
332
  return response
143
333
  pattern = r'^v\d+\.\d+\.\d+$'
144
334
  if not re.match(pattern, self.version):
@@ -154,6 +344,33 @@ class ByoGs:
154
344
  response['error'] = False
155
345
  return response
156
346
 
347
+ # CRUD methods
348
+ def build(self) -> bool:
349
+ """
350
+ Build the image
351
+ 1. Check Dependencies
352
+ 2. Login to Snapser Registry
353
+ 3. Build your snap
354
+ """
355
+ if not self._check_dependencies() or not self._docker_login() or \
356
+ not self._docker_build():
357
+ return False
358
+ return True
359
+
360
+ def push(self) -> bool:
361
+ """
362
+ Tag the image
363
+ 1. Check Dependencies
364
+ 2. Login to Snapser Registry
365
+ 3. Tag the snap
366
+ 4. Push your snap
367
+ """
368
+ if not self._check_dependencies() or not self._docker_login() or \
369
+ not self._docker_tag() or not self._docker_push():
370
+ return False
371
+ return True
372
+
373
+ # Upper echelon commands
157
374
  def create(self) -> bool:
158
375
  """
159
376
  Create a new game server
@@ -196,191 +413,18 @@ class ByoGs:
196
413
  error(f"Exception: Unable to create your game server {e}")
197
414
  return False
198
415
 
199
- def build(self) -> bool:
200
- """
201
- Build your game server image
202
- """
203
- # Get the data
204
- ecr_repo_url = self.token_parts[0]
205
- ecr_repo_username = self.token_parts[1]
206
- ecr_repo_token = self.token_parts[2]
207
- image_tag = f'{self.sid}.{self.tag}'
208
- full_ecr_repo_url = f'{ecr_repo_url}:{image_tag}'
209
- build_platform = ByoGs.DEFAULT_BUILD_PLATFORM
210
- if len(self.token_parts) == 4:
211
- build_platform = self.token_parts[3]
212
- try:
213
- # Check dependencies
214
- with Progress(
215
- SpinnerColumn(),
216
- TextColumn("[progress.description]{task.description}"),
217
- transient=True,
218
- ) as progress:
219
- progress.add_task(
220
- description='Checking dependencies...', total=None)
221
- try:
222
- subprocess.run([
223
- "docker", "--version"
224
- ], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT, check=False)
225
- except subprocess.CalledProcessError:
226
- error('Docker not present')
227
- return False
228
- success('Dependencies Verified')
229
-
230
- # Login to Snapser Registry
231
- with Progress(
232
- SpinnerColumn(),
233
- TextColumn("[progress.description]{task.description}"),
234
- transient=True,
235
- ) as progress:
236
- progress.add_task(
237
- description='Logging into Snapser Image Registry...', total=None)
238
- if platform == 'win32':
239
- response = subprocess.run([
240
- 'docker', 'login', '--username', ecr_repo_username,
241
- '--password', ecr_repo_token, ecr_repo_url
242
- ], shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT, check=False)
243
- else:
244
- response = subprocess.run([
245
- f'echo "{ecr_repo_token}" | docker login '
246
- f'--username {ecr_repo_username} --password-stdin {ecr_repo_url}'
247
- ], shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT, check=False)
248
- if response.returncode:
249
- error(
250
- 'Unable to connect to the Snapser Container Repository. '
251
- 'Please confirm if docker is running or try restarting docker'
252
- )
253
- return False
254
- success('Login Successful')
255
-
256
- # Build your snap
257
- with Progress(
258
- SpinnerColumn(),
259
- TextColumn("[progress.description]{task.description}"),
260
- transient=True,
261
- ) as progress:
262
- progress.add_task(
263
- description='Building your snap...', total=None)
264
- if platform == "win32":
265
- response = subprocess.run([
266
- # f"docker build --no-cache -t {tag} {path}"
267
- 'docker', 'build', '--platform', build_platform, '-t', image_tag, self.path
268
- ], shell=True, check=False)
269
- # stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
270
- else:
271
- response = subprocess.run([
272
- # f"docker build --no-cache -t {tag} {path}"
273
- f"docker build --platform {build_platform} -t {image_tag} {self.path}"
274
- ], shell=True, check=False)
275
- # stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
276
- if response.returncode:
277
- error('Unable to build docker')
278
- return False
279
- success('Build Successful')
280
-
281
- # Tag the repo
282
- with Progress(
283
- SpinnerColumn(),
284
- TextColumn("[progress.description]{task.description}"),
285
- transient=True,
286
- ) as progress:
287
- progress.add_task(
288
- description='Tagging your snap...', total=None)
289
- if platform == "win32":
290
- response = subprocess.run([
291
- 'docker', 'tag', image_tag, full_ecr_repo_url
292
- ], shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT, check=False)
293
- else:
294
- response = subprocess.run([
295
- f"docker tag {image_tag} {full_ecr_repo_url}"
296
- ], shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT, check=False)
297
- if response.returncode:
298
- error('Unable to tag your snap')
299
- return False
300
- success('Tag Successful')
301
-
302
- return True
303
- except subprocess.CalledProcessError:
304
- error('CLI Error')
305
- return False
306
-
307
- def push(self) -> bool:
308
- """
309
- Push your game server image
310
- """
311
- ecr_repo_url = self.token_parts[0]
312
- image_tag = f'{self.sid}.{self.tag}'
313
- full_ecr_repo_url = f'{ecr_repo_url}:{image_tag}'
314
-
315
- # Push the image
316
- with Progress(
317
- SpinnerColumn(),
318
- TextColumn("[progress.description]{task.description}"),
319
- transient=True,
320
- ) as progress:
321
- progress.add_task(description='Pushing your snap...', total=None)
322
- if platform == "win32":
323
- response = subprocess.run([
324
- 'docker', 'push', full_ecr_repo_url
325
- ], shell=True, check=False)
326
- # stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
327
- else:
328
- response = subprocess.run([
329
- f"docker push {full_ecr_repo_url}"
330
- ], shell=True, check=False)
331
- # stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
332
- if response.returncode:
333
- error('Unable to push your snap')
334
- return False
335
- success('Snap Upload Successful')
336
- return True
337
-
338
- # def upload_docs(self) -> bool:
339
- # '''
340
- # Note this step is optional hence we always respond with a True
341
- # '''
342
- # # Push the swagger.json
343
- # with Progress(
344
- # SpinnerColumn(),
345
- # TextColumn("[progress.description]{task.description}"),
346
- # transient=True,
347
- # ) as progress:
348
- # progress.add_task(description=f'Uploading your API Json...', total=None)
349
- # try:
350
- # attachment_file = open(f"{self.path}/swagger.json", "rb")
351
- # test_res = requests.post(f"{self.base_url}/v1/snapser-api/byogs/{self.sid}/upload/{self.tag}/openapispec", files = {"attachment": attachment_file}, headers={'api-key': self.api_key})
352
- # if test_res.ok:
353
- # success('Uploaded Swagger.json')
354
- # else:
355
- # response_json = test_res.json()
356
- # error(response_json['details'][0])
357
- # except Exception as e:
358
- # info('Unable to find swagger.json at ' + self.path + str(e))
359
-
360
- # # Push the README.md
361
- # with Progress(
362
- # SpinnerColumn(),
363
- # TextColumn("[progress.description]{task.description}"),
364
- # transient=True,
365
- # ) as progress:
366
- # progress.add_task(description=f'Uploading your README...', total=None)
367
- # try:
368
- # attachment_file = open(f"{self.path}/README.md", "rb")
369
- # test_res = requests.post(f"{self.base_url}/v1/snapser-api/byogs/{self.sid}/upload/{self.tag}/markdown", files = {"attachment": attachment_file}, headers={'api-key': self.api_key})
370
- # if test_res.ok:
371
- # success('Uploaded README.md')
372
- # else:
373
- # error('Unable to upload your README.md')
374
- # except Exception as e:
375
- # info('Unable to find README.md at ' + self.path + str(e))
376
- # return True
377
-
378
416
  def publish_image(self) -> bool:
379
417
  """
380
- Publish your game server image
418
+ Publish the image
419
+ 1. Check Dependencies
420
+ 2. Login to Snapser Registry
421
+ 3. Build your snap
422
+ 4. Tag the repo
423
+ 5. Push the image
424
+ 6. Upload swagger.json
381
425
  """
382
- # if not self.build() or not self.push() or not self.upload_docs():
383
- if not self.build() or not self.push():
426
+ if not self._check_dependencies() or not self._docker_login() or \
427
+ not self._docker_build() or not self._docker_tag() or not self._docker_push():
384
428
  return False
385
429
  return True
386
430
 
@@ -398,7 +442,7 @@ class ByoGs:
398
442
  try:
399
443
  payload = {
400
444
  "version": self.version,
401
- "image_tag": self.tag,
445
+ "image_tag": self.input_tag,
402
446
  "http_port": self.http_port,
403
447
  }
404
448
  if self.debug_port: