snapctl 0.4.5__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
@@ -1,341 +1,473 @@
1
+ """
2
+ BYOGS CLI commands
3
+ """
1
4
  import base64
5
+ from binascii import Error as BinasciiError
2
6
  import json
3
7
  import os
4
8
  import re
5
- import requests
6
9
  import subprocess
7
-
8
- from rich.progress import Progress, SpinnerColumn, TextColumn
9
10
  from sys import platform
10
11
  from typing import Union
12
+ import requests
13
+ from requests.exceptions import RequestException
14
+
15
+ from rich.progress import Progress, SpinnerColumn, TextColumn
16
+ from snapctl.config.constants import SERVER_CALL_TIMEOUT
11
17
  from snapctl.config.constants import ERROR_SERVICE_VERSION_EXISTS, ERROR_TAG_NOT_AVAILABLE
12
18
  from snapctl.types.definitions import ResponseType
13
19
  from snapctl.utils.echo import error, success
14
20
  from snapctl.utils.helper import get_composite_token
15
21
 
22
+
16
23
  class ByoGs:
17
- ID_PREFIX = 'byogs-'
18
- SUBCOMMANDS = ['create', 'publish-image', 'publish-version']
19
- PLATFORMS = ['linux/amd64']
20
- LANGUAGES = ['go', 'python', 'ruby', 'c#', 'c++', 'rust', 'java', 'node']
21
- DEFAULT_BUILD_PLATFORM = 'linux/amd64'
24
+ """
25
+ BYOGS CLI commands
26
+ """
27
+ ID_PREFIX = 'byogs-'
28
+ SUBCOMMANDS = [
29
+ 'build', 'push',
30
+ 'create', 'publish-image', 'publish-version',
31
+ ]
32
+ PLATFORMS = ['linux/amd64']
33
+ LANGUAGES = ['go', 'python', 'ruby', 'c#', 'c++', 'rust', 'java', 'node']
34
+ DEFAULT_BUILD_PLATFORM = 'linux/amd64'
35
+ SID_CHARACTER_LIMIT = 47
36
+ TAG_CHARACTER_LIMIT = 80
22
37
 
23
- def __init__(self, subcommand: str, base_url: str, api_key: str, sid: str, name: str, desc: str, platform: str, language: str, tag: Union[str, None], path: Union[str, None], dockerfile: str, version: Union[str, None], http_port: Union[int, None]) -> None:
24
- self.subcommand: str = subcommand
25
- self.base_url: str = base_url
26
- self.api_key: str = api_key
27
- self.sid: str = sid
28
- self.name: str = name
29
- self.desc: str = desc
30
- self.platform: str = platform
31
- self.language: str = language
32
- self.token: Union[str, None] = get_composite_token(base_url, api_key, 'byogs', {'service_id': sid}) if subcommand != 'create' else None
33
- self.token_parts: Union[list, None] = ByoGs.get_token_values(self.token) if self.token is not None else None
34
- self.tag: Union[str, None] = tag
35
- self.path: Union[str, None] = path
36
- self.dockerfile: str = dockerfile
37
- self.version: Union[str, None] = version
38
- self.http_port: Union[int, None] = http_port
38
+ def __init__(
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],
41
+ dockerfile: str, version: Union[str, None], http_port: Union[int, None],
42
+ debug_port: Union[int, None]
43
+ ) -> None:
44
+ self.subcommand: str = subcommand
45
+ self.base_url: str = base_url
46
+ self.api_key: str = api_key
47
+ self.sid: str = sid
48
+ self.name: str = name
49
+ self.desc: str = desc
50
+ self.platform_type: str = platform_type
51
+ self.language: str = language
52
+ if subcommand != 'create':
53
+ self.token: Union[str, None] = get_composite_token(
54
+ base_url, api_key, 'byogs', {'service_id': sid})
55
+ else:
56
+ self.token: Union[str, None] = None
57
+ self.token_parts: Union[list, None] = ByoGs._get_token_values(
58
+ self.token) if self.token is not None else None
59
+ self.input_tag: Union[str, None] = input_tag
60
+ self.path: Union[str, None] = path
61
+ self.dockerfile: str = dockerfile
62
+ self.version: Union[str, None] = version
63
+ self.http_port: Union[int, None] = http_port
64
+ self.debug_port: Union[int, None] = debug_port
39
65
 
40
- @staticmethod
41
- def get_token_values(token: str) -> None | list:
42
- try:
43
- input_token = base64.b64decode(token).decode('ascii')
44
- token_parts = input_token.split('|')
45
- # url|web_app_token|service_id|ecr_repo_url|ecr_repo_username|ecr_repo_token
46
- # url = self.token_parts[0]
47
- # web_app_token = self.token_parts[1]
48
- # service_id = self.token_parts[2]
49
- # ecr_repo_url = self.token_parts[3]
50
- # ecr_repo_username = self.token_parts[4]
51
- # ecr_repo_token = self.token_parts[5]
52
- # platform = self.token_parts[6]
53
- if len(token_parts) >= 3:
54
- return token_parts
55
- except Exception:
56
- pass
57
- return None
66
+ # Protected methods
58
67
 
59
- def validate_input(self) -> ResponseType:
60
- response: ResponseType = {
61
- 'error': True,
62
- 'msg': '',
63
- 'data': []
64
- }
65
- # Check subcommand
66
- if not self.subcommand in ByoGs.SUBCOMMANDS:
67
- response['msg'] = f"Invalid command. Valid commands are {', '.join(ByoGs.SUBCOMMANDS)}."
68
- return response
69
- # Validate the SID
70
- if not self.sid.startswith(ByoGs.ID_PREFIX):
71
- response['msg'] = f"Invalid Game Server ID. Valid Game Server IDs start with {ByoGs.ID_PREFIX}."
72
- return response
73
- # Validation for subcommands
74
- if self.subcommand == 'create':
75
- if self.name == '':
76
- response['msg'] = f"Missing name"
77
- return response
78
- if self.language not in ByoGs.LANGUAGES:
79
- response['msg'] = f"Invalid language. Valid languages are {', '.join(ByoGs.LANGUAGES)}."
80
- return response
81
- if self.platform not in ByoGs.PLATFORMS:
82
- response['msg'] = f"Invalid platform. Valid platforms are {', '.join(ByoGs.PLATFORMS)}."
83
- return response
84
- else:
85
- if self.token_parts is None:
86
- response['msg'] = 'Invalid token. Please reach out to your support team'
87
- return response
88
- # Check tag
89
- if self.tag is None or len(self.tag.split()) > 1 or len(self.tag) > 25:
90
- response['msg'] = f"Tag should be a single word with maximum of 25 characters"
91
- return response
92
- if self.subcommand == 'publish-image':
93
- if not self.path:
94
- response['msg'] = f"Missing path"
95
- return response
96
- # Check path
97
- if not os.path.isfile(f"{self.path}/{self.dockerfile}"):
98
- response['msg'] = f"Unable to find {self.dockerfile} at path {self.path}"
99
- return response
100
- elif self.subcommand == 'publish-version':
101
- if not self.version:
102
- response['msg'] = f"Missing version"
103
- return response
104
- if not self.http_port:
105
- response['msg'] = f"Missing Ingress HTTP Port"
106
- return response
107
- pattern = r'^v\d+\.\d+\.\d+$'
108
- if not re.match(pattern, self.version):
109
- response['msg'] = f"Version should be in the format vX.X.X"
110
- return response
111
- if not self.http_port.isdigit():
112
- response['msg'] = f"Ingress HTTP Port should be a number"
113
- return response
114
- # Send success
115
- response['error'] = False
116
- return response
68
+ @staticmethod
69
+ def _get_token_values(token: str) -> None | list:
70
+ """
71
+ Get the token values
72
+ """
73
+ try:
74
+ input_token = base64.b64decode(token).decode('ascii')
75
+ token_parts = input_token.split('|')
76
+ # url|web_app_token|service_id|ecr_repo_url|ecr_repo_username|ecr_repo_token
77
+ # url = self.token_parts[0]
78
+ # web_app_token = self.token_parts[1]
79
+ # service_id = self.token_parts[2]
80
+ # ecr_repo_url = self.token_parts[3]
81
+ # ecr_repo_username = self.token_parts[4]
82
+ # ecr_repo_token = self.token_parts[5]
83
+ # platform = self.token_parts[6]
84
+ if len(token_parts) >= 3:
85
+ return token_parts
86
+ except BinasciiError:
87
+ pass
88
+ return None
117
89
 
118
- def create(self) -> bool:
119
- with Progress(
120
- SpinnerColumn(),
121
- TextColumn("[progress.description]{task.description}"),
122
- transient=True,
123
- ) as progress:
124
- progress.add_task(description=f'Creating your game server...', total=None)
125
- try:
126
- payload = {
127
- "service_id": self.sid,
128
- "name": self.name,
129
- "description": self.desc,
130
- "platform": self.platform,
131
- "language": self.language,
132
- }
133
- res = requests.post(f"{self.base_url}/v1/snapser-api/byogs", json=payload, headers={'api-key': self.api_key})
134
- if res.ok:
135
- return True
136
- response_json = res.json()
137
- if "api_error_code" in response_json:
138
- if response_json['api_error_code'] == ERROR_SERVICE_VERSION_EXISTS:
139
- error('Version already exists. Please update your version and try again')
140
- if response_json['api_error_code'] == ERROR_TAG_NOT_AVAILABLE:
141
- error('Invalid tag. Please use the correct tag')
142
- else:
143
- error(f'Server error: {json.dumps(response_json, indent=2)}')
144
- except Exception as e:
145
- error("Exception: Unable to create your game server")
146
- return False
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
147
112
 
148
- def build(self) -> bool:
149
- # Get the data
150
- ecr_repo_url = self.token_parts[0]
151
- ecr_repo_username = self.token_parts[1]
152
- ecr_repo_token = self.token_parts[2]
153
- image_tag = f'{self.sid}.{self.tag}'
154
- full_ecr_repo_url = f'{ecr_repo_url}:{image_tag}'
155
- build_platform = ByoGs.DEFAULT_BUILD_PLATFORM
156
- if len(self.token_parts) == 4:
157
- build_platform = self.token_parts[3]
158
- try:
159
- # Check dependencies
160
- with Progress(
161
- SpinnerColumn(),
162
- TextColumn("[progress.description]{task.description}"),
163
- transient=True,
164
- ) as progress:
165
- progress.add_task(description=f'Checking dependencies...', total=None)
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]
166
118
  try:
167
- subprocess.run([
168
- "docker", "--version"
169
- ], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
170
- except:
171
- error('Docker not present')
172
- return False
173
- success('Dependencies Verified')
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
174
148
 
175
- # Login to Snapser Registry
176
- with Progress(
177
- SpinnerColumn(),
178
- TextColumn("[progress.description]{task.description}"),
179
- transient=True,
180
- ) as progress:
181
- progress.add_task(description=f'Logging into Snapser Image Registry...', total=None)
182
- if platform == 'win32':
183
- response = subprocess.run([
184
- 'docker', 'login', '--username', ecr_repo_username, '--password', ecr_repo_token, ecr_repo_url
185
- ], shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
186
- else:
187
- response = subprocess.run([
188
- f'echo "{ecr_repo_token}" | docker login --username {ecr_repo_username} --password-stdin {ecr_repo_url}'
189
- ], shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
190
- if response.returncode:
191
- error('Unable to connect to the Snapser Container Repository. Please get the latest token from the Web app')
192
- return False
193
- success('Login Successful')
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
194
184
 
195
- # Build your snap
196
- with Progress(
197
- SpinnerColumn(),
198
- TextColumn("[progress.description]{task.description}"),
199
- transient=True,
200
- ) as progress:
201
- progress.add_task(description=f'Building your snap...', total=None)
202
- if platform == "win32":
203
- response = subprocess.run([
204
- #f"docker build --no-cache -t {tag} {path}"
205
- 'docker', 'build', '--platform', build_platform, '-t', image_tag, self.path
206
- ], shell=True, )#stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
207
- else:
208
- response = subprocess.run([
209
- #f"docker build --no-cache -t {tag} {path}"
210
- f"docker build --platform {build_platform} -t {image_tag} {self.path}"
211
- ], shell=True, )#stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
212
- if response.returncode:
213
- error('Unable to build docker')
214
- return False
215
- success('Build Successful')
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
216
215
 
217
- # Tag the repo
218
- with Progress(
219
- SpinnerColumn(),
220
- TextColumn("[progress.description]{task.description}"),
221
- transient=True,
222
- ) as progress:
223
- progress.add_task(description=f'Tagging your snap...', total=None)
224
- if platform == "win32":
225
- response = subprocess.run([
226
- 'docker', 'tag', image_tag, full_ecr_repo_url
227
- ], shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
228
- else:
229
- response = subprocess.run([
230
- f"docker tag {image_tag} {full_ecr_repo_url}"
231
- ], shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
232
- if response.returncode:
233
- error('Unable to tag your snap')
234
- return False
235
- success('Tag Successful')
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}'
236
221
 
237
- return True
238
- except:
239
- error('CLI Error')
240
- return False
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
241
248
 
242
- def push(self) -> bool:
243
- ecr_repo_url = self.token_parts[0]
244
- image_tag = f'{self.sid}.{self.tag}'
245
- full_ecr_repo_url = f'{ecr_repo_url}:{image_tag}'
249
+ # Public methods
246
250
 
247
- # Push the image
248
- with Progress(
249
- SpinnerColumn(),
250
- TextColumn("[progress.description]{task.description}"),
251
- transient=True,
252
- ) as progress:
253
- progress.add_task(description=f'Pushing your snap...', total=None)
254
- if platform == "win32":
255
- response = subprocess.run([
256
- 'docker', 'push', full_ecr_repo_url
257
- ], shell=True, )#stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
258
- else:
259
- response = subprocess.run([
260
- f"docker push {full_ecr_repo_url}"
261
- ], shell=True, )#stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
262
- if response.returncode:
263
- error('Unable to push your snap')
264
- return False
265
- success('Snap Upload Successful')
266
- return True
251
+ # Validator
252
+ def validate_input(self) -> ResponseType:
253
+ """
254
+ Validator
255
+ """
256
+ response: ResponseType = {
257
+ 'error': True,
258
+ 'msg': '',
259
+ 'data': []
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
265
+ # Check subcommand
266
+ if not self.subcommand in ByoGs.SUBCOMMANDS:
267
+ response['msg'] = f"Invalid command. Valid commands are {', '.join(ByoGs.SUBCOMMANDS)}."
268
+ return response
269
+ # Validate the SID
270
+ if not self.sid.startswith(ByoGs.ID_PREFIX):
271
+ response['msg'] = (
272
+ "Invalid Game Server ID. Valid Game Server IDs start "
273
+ f"with {ByoGs.ID_PREFIX}."
274
+ )
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
282
+ # Validation for subcommands
283
+ if self.subcommand == 'create':
284
+ if self.name == '':
285
+ response['msg'] = "Missing name"
286
+ return response
287
+ if self.language not in ByoGs.LANGUAGES:
288
+ response['msg'] = (
289
+ "Invalid language. Valid languages "
290
+ f"are {', '.join(ByoGs.LANGUAGES)}."
291
+ )
292
+ return response
293
+ if self.platform_type not in ByoGs.PLATFORMS:
294
+ response['msg'] = (
295
+ "Invalid platform. Valid platforms "
296
+ f"are {', '.join(ByoGs.PLATFORMS)}."
297
+ )
298
+ return response
299
+ else:
300
+ if self.token_parts is None:
301
+ response['msg'] = 'Invalid token. Please reach out to your support team'
302
+ return response
303
+ # Check tag
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
+ )
310
+ return response
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
315
+ if not self.path:
316
+ response['msg'] = "Missing required parameter: path"
317
+ return response
318
+ # Check path
319
+ if not os.path.isfile(f"{self.path}/{self.dockerfile}"):
320
+ response['msg'] = f"Unable to find {self.dockerfile} at path {self.path}"
321
+ return response
322
+ elif self.subcommand == 'push':
323
+ if not self.input_tag:
324
+ response['msg'] = "Missing required parameter: tag"
325
+ return response
326
+ elif self.subcommand == 'publish-version':
327
+ if not self.version:
328
+ response['msg'] = "Missing required parameter: version"
329
+ return response
330
+ if not self.http_port:
331
+ response['msg'] = "Missing required parameter: Ingress HTTP Port"
332
+ return response
333
+ pattern = r'^v\d+\.\d+\.\d+$'
334
+ if not re.match(pattern, self.version):
335
+ response['msg'] = "Version should be in the format vX.X.X"
336
+ return response
337
+ if not self.http_port.isdigit():
338
+ response['msg'] = "Ingress HTTP Port should be a number"
339
+ return response
340
+ if self.debug_port and not self.debug_port.isdigit():
341
+ response['msg'] = "Debug Port should be a number"
342
+ return response
343
+ # Send success
344
+ response['error'] = False
345
+ return response
267
346
 
268
- # def upload_docs(self) -> bool:
269
- # '''
270
- # Note this step is optional hence we always respond with a True
271
- # '''
272
- # # Push the swagger.json
273
- # with Progress(
274
- # SpinnerColumn(),
275
- # TextColumn("[progress.description]{task.description}"),
276
- # transient=True,
277
- # ) as progress:
278
- # progress.add_task(description=f'Uploading your API Json...', total=None)
279
- # try:
280
- # dfile = open(f"{self.path}/swagger.json", "rb")
281
- # test_res = requests.post(f"{self.base_url}/v1/snapser-api/byogs/{self.sid}/upload/{self.tag}/openapispec", files = {"attachment": dfile}, headers={'api-key': self.api_key})
282
- # if test_res.ok:
283
- # success('Uploaded Swagger.json')
284
- # else:
285
- # response_json = test_res.json()
286
- # error(response_json['details'][0])
287
- # except Exception as e:
288
- # info('Unable to find swagger.json at ' + self.path + str(e))
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
289
359
 
290
- # # Push the README.md
291
- # with Progress(
292
- # SpinnerColumn(),
293
- # TextColumn("[progress.description]{task.description}"),
294
- # transient=True,
295
- # ) as progress:
296
- # progress.add_task(description=f'Uploading your README...', total=None)
297
- # try:
298
- # dfile = open(f"{self.path}/README.md", "rb")
299
- # test_res = requests.post(f"{self.base_url}/v1/snapser-api/byogs/{self.sid}/upload/{self.tag}/markdown", files = {"attachment": dfile}, headers={'api-key': self.api_key})
300
- # if test_res.ok:
301
- # success('Uploaded README.md')
302
- # else:
303
- # error('Unable to upload your README.md')
304
- # except Exception as e:
305
- # info('Unable to find README.md at ' + self.path + str(e))
306
- # return True
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
307
372
 
308
- def publish_image(self) -> bool:
309
- # if not self.build() or not self.push() or not self.upload_docs():
310
- if not self.build() or not self.push():
311
- return False
312
- return True
373
+ # Upper echelon commands
374
+ def create(self) -> bool:
375
+ """
376
+ Create a new game server
377
+ """
378
+ with Progress(
379
+ SpinnerColumn(),
380
+ TextColumn("[progress.description]{task.description}"),
381
+ transient=True,
382
+ ) as progress:
383
+ progress.add_task(
384
+ description='Creating your game server...', total=None)
385
+ try:
386
+ payload = {
387
+ "service_id": self.sid,
388
+ "name": self.name,
389
+ "description": self.desc,
390
+ "platform": self.platform_type,
391
+ "language": self.language,
392
+ }
393
+ res = requests.post(
394
+ f"{self.base_url}/v1/snapser-api/byogs",
395
+ json=payload, headers={'api-key': self.api_key},
396
+ timeout=SERVER_CALL_TIMEOUT
397
+ )
398
+ if res.ok:
399
+ return True
400
+ response_json = res.json()
401
+ if "api_error_code" in response_json:
402
+ if response_json['api_error_code'] == ERROR_SERVICE_VERSION_EXISTS:
403
+ error(
404
+ 'Version already exists. Please update your version and try again'
405
+ )
406
+ if response_json['api_error_code'] == ERROR_TAG_NOT_AVAILABLE:
407
+ error('Invalid tag. Please use the correct tag')
408
+ else:
409
+ error(
410
+ f'Server error: {json.dumps(response_json, indent=2)}'
411
+ )
412
+ except RequestException as e:
413
+ error(f"Exception: Unable to create your game server {e}")
414
+ return False
313
415
 
314
- def publish_version(self) -> bool:
315
- with Progress(
316
- SpinnerColumn(),
317
- TextColumn("[progress.description]{task.description}"),
318
- transient=True,
319
- ) as progress:
320
- progress.add_task(description=f'Publishing your snap...', total=None)
321
- try:
322
- payload = {
323
- "version": self.version,
324
- "image_tag": self.tag,
325
- "http_port": self.http_port,
326
- }
327
- res = requests.post(f"{self.base_url}/v1/snapser-api/byogs/{self.sid}/versions", json=payload, headers={'api-key': self.api_key})
328
- if res.ok:
329
- return True
330
- response_json = res.json()
331
- if "api_error_code" in response_json:
332
- if response_json['api_error_code'] == ERROR_SERVICE_VERSION_EXISTS:
333
- error('Version already exists. Please update your version and try again')
334
- if response_json['api_error_code'] == ERROR_TAG_NOT_AVAILABLE:
335
- error('Invalid tag. Please use the correct tag')
336
- else:
337
- error(f'Server error: {json.dumps(response_json, indent=2)}')
338
- except Exception as e:
339
- error("Exception: Unable to publish a version for your snap")
340
- return False
416
+ def publish_image(self) -> bool:
417
+ """
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
425
+ """
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():
428
+ return False
429
+ return True
341
430
 
431
+ def publish_version(self) -> bool:
432
+ """
433
+ Publish your game server version
434
+ """
435
+ with Progress(
436
+ SpinnerColumn(),
437
+ TextColumn("[progress.description]{task.description}"),
438
+ transient=True,
439
+ ) as progress:
440
+ progress.add_task(
441
+ description='Publishing your snap...', total=None)
442
+ try:
443
+ payload = {
444
+ "version": self.version,
445
+ "image_tag": self.input_tag,
446
+ "http_port": self.http_port,
447
+ }
448
+ if self.debug_port:
449
+ payload['debug_port'] = self.debug_port
450
+ res = requests.post(
451
+ f"{self.base_url}/v1/snapser-api/byogs/{self.sid}/versions",
452
+ json=payload, headers={'api-key': self.api_key},
453
+ timeout=SERVER_CALL_TIMEOUT
454
+ )
455
+ if res.ok:
456
+ return True
457
+ response_json = res.json()
458
+ if "api_error_code" in response_json:
459
+ if response_json['api_error_code'] == ERROR_SERVICE_VERSION_EXISTS:
460
+ error(
461
+ 'Version already exists. Please update your version and try again'
462
+ )
463
+ if response_json['api_error_code'] == ERROR_TAG_NOT_AVAILABLE:
464
+ error('Invalid tag. Please use the correct tag')
465
+ else:
466
+ error(
467
+ f'Server error: {json.dumps(response_json, indent=2)}'
468
+ )
469
+ except RequestException as e:
470
+ error(
471
+ f"Exception: Unable to publish a version for your snap {e}"
472
+ )
473
+ return False