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 +239 -195
- snapctl/commands/byosnap.py +251 -160
- snapctl/commands/snapend.py +5 -1
- snapctl/config/constants.py +1 -1
- snapctl/main.py +134 -72
- snapctl/utils/helper.py +6 -3
- {snapctl-0.22.0.dist-info → snapctl-0.22.1.dist-info}/METADATA +111 -22
- {snapctl-0.22.0.dist-info → snapctl-0.22.1.dist-info}/RECORD +10 -10
- {snapctl-0.22.0.dist-info → snapctl-0.22.1.dist-info}/WHEEL +0 -0
- {snapctl-0.22.0.dist-info → snapctl-0.22.1.dist-info}/entry_points.txt +0 -0
snapctl/commands/byosnap.py
CHANGED
|
@@ -14,7 +14,8 @@ from requests.exceptions import RequestException
|
|
|
14
14
|
|
|
15
15
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
16
16
|
from snapctl.config.constants import SERVER_CALL_TIMEOUT
|
|
17
|
-
from snapctl.config.constants import ERROR_SERVICE_VERSION_EXISTS, ERROR_TAG_NOT_AVAILABLE,
|
|
17
|
+
from snapctl.config.constants import ERROR_SERVICE_VERSION_EXISTS, ERROR_TAG_NOT_AVAILABLE, \
|
|
18
|
+
ERROR_ADD_ON_NOT_ENABLED
|
|
18
19
|
from snapctl.types.definitions import ResponseType
|
|
19
20
|
from snapctl.utils.echo import error, success, info
|
|
20
21
|
from snapctl.utils.helper import get_composite_token
|
|
@@ -25,14 +26,19 @@ class ByoSnap:
|
|
|
25
26
|
CLI commands exposed for a BYOSnap
|
|
26
27
|
"""
|
|
27
28
|
ID_PREFIX = 'byosnap-'
|
|
28
|
-
SUBCOMMANDS = [
|
|
29
|
+
SUBCOMMANDS = [
|
|
30
|
+
'build', 'push', 'upload-docs',
|
|
31
|
+
'create', 'publish-image', 'publish-version',
|
|
32
|
+
]
|
|
29
33
|
PLATFORMS = ['linux/arm64', 'linux/amd64']
|
|
30
34
|
LANGUAGES = ['go', 'python', 'ruby', 'c#', 'c++', 'rust', 'java', 'node']
|
|
31
35
|
DEFAULT_BUILD_PLATFORM = 'linux/arm64'
|
|
36
|
+
SID_CHARACTER_LIMIT = 47
|
|
37
|
+
TAG_CHARACTER_LIMIT = 80
|
|
32
38
|
|
|
33
39
|
def __init__(
|
|
34
|
-
self, subcommand: str, base_url: str, api_key: str, sid: str, name: str,
|
|
35
|
-
desc: str, platform_type: str, language: str,
|
|
40
|
+
self, subcommand: str, base_url: str, api_key: str | None, sid: str, name: str,
|
|
41
|
+
desc: str, platform_type: str, language: str, input_tag: Union[str, None],
|
|
36
42
|
path: Union[str, None], dockerfile: str, prefix: str, version: Union[str, None],
|
|
37
43
|
http_port: Union[int, None]
|
|
38
44
|
) -> None:
|
|
@@ -45,21 +51,24 @@ class ByoSnap:
|
|
|
45
51
|
self.platform_type: str = platform_type
|
|
46
52
|
self.language: str = language
|
|
47
53
|
if subcommand != 'create':
|
|
48
|
-
self.token: Union[str, None] = get_composite_token(
|
|
49
|
-
|
|
54
|
+
self.token: Union[str, None] = get_composite_token(
|
|
55
|
+
base_url, api_key,
|
|
56
|
+
'byosnap', {'service_id': sid}
|
|
57
|
+
)
|
|
50
58
|
else:
|
|
51
59
|
self.token: Union[str, None] = None
|
|
52
|
-
self.token_parts: Union[list, None] = ByoSnap.
|
|
60
|
+
self.token_parts: Union[list, None] = ByoSnap._get_token_values(
|
|
53
61
|
self.token) if self.token is not None else None
|
|
54
|
-
self.
|
|
62
|
+
self.input_tag: Union[str, None] = input_tag
|
|
55
63
|
self.path: Union[str, None] = path
|
|
56
64
|
self.dockerfile: str = dockerfile
|
|
57
65
|
self.prefix: str = prefix
|
|
58
66
|
self.version: Union[str, None] = version
|
|
59
67
|
self.http_port: Union[int, None] = http_port
|
|
60
68
|
|
|
69
|
+
# Protected methods
|
|
61
70
|
@staticmethod
|
|
62
|
-
def
|
|
71
|
+
def _get_token_values(token: str) -> None | list:
|
|
63
72
|
"""
|
|
64
73
|
Method to break open the token
|
|
65
74
|
"""
|
|
@@ -80,152 +89,10 @@ class ByoSnap:
|
|
|
80
89
|
pass
|
|
81
90
|
return None
|
|
82
91
|
|
|
83
|
-
def
|
|
92
|
+
def _check_dependencies(self) -> bool:
|
|
84
93
|
"""
|
|
85
|
-
|
|
94
|
+
Check application dependencies
|
|
86
95
|
"""
|
|
87
|
-
response: ResponseType = {
|
|
88
|
-
'error': True,
|
|
89
|
-
'msg': '',
|
|
90
|
-
'data': []
|
|
91
|
-
}
|
|
92
|
-
# Check subcommand
|
|
93
|
-
if not self.subcommand in ByoSnap.SUBCOMMANDS:
|
|
94
|
-
response['msg'] = (
|
|
95
|
-
"Invalid command. Valid commands ",
|
|
96
|
-
f"are {', '.join(ByoSnap.SUBCOMMANDS)}."
|
|
97
|
-
)
|
|
98
|
-
return response
|
|
99
|
-
# Validate the SID
|
|
100
|
-
if not self.sid.startswith(ByoSnap.ID_PREFIX):
|
|
101
|
-
response['msg'] = f"Invalid Snap ID. Valid Snap IDs start with {ByoSnap.ID_PREFIX}."
|
|
102
|
-
return response
|
|
103
|
-
# Validation for subcommands
|
|
104
|
-
if self.subcommand == 'create':
|
|
105
|
-
if self.name == '':
|
|
106
|
-
response['msg'] = "Missing name"
|
|
107
|
-
return response
|
|
108
|
-
if self.language not in ByoSnap.LANGUAGES:
|
|
109
|
-
response['msg'] = (
|
|
110
|
-
"Invalid language. Valid languages are "
|
|
111
|
-
f"{', '.join(ByoSnap.LANGUAGES)}."
|
|
112
|
-
)
|
|
113
|
-
return response
|
|
114
|
-
if self.platform_type not in ByoSnap.PLATFORMS:
|
|
115
|
-
response['msg'] = (
|
|
116
|
-
"Invalid platform. Valid platforms are "
|
|
117
|
-
f"{', '.join(ByoSnap.PLATFORMS)}."
|
|
118
|
-
)
|
|
119
|
-
return response
|
|
120
|
-
else:
|
|
121
|
-
# Check the token
|
|
122
|
-
if self.token_parts is None:
|
|
123
|
-
response['msg'] = 'Invalid token. Please reach out to your support team.'
|
|
124
|
-
return response
|
|
125
|
-
# Check tag
|
|
126
|
-
if self.tag is None or len(self.tag.split()) > 1 or len(self.tag) > 25:
|
|
127
|
-
response['msg'] = "Tag should be a single word with maximum of 25 characters"
|
|
128
|
-
return response
|
|
129
|
-
if self.subcommand == 'publish-image':
|
|
130
|
-
if not self.path:
|
|
131
|
-
response['msg'] = "Missing required parameter: path"
|
|
132
|
-
return response
|
|
133
|
-
# Check path
|
|
134
|
-
if not os.path.isfile(f"{self.path}/{self.dockerfile}"):
|
|
135
|
-
response['msg'] = f"Unable to find {self.dockerfile} at path {self.path}"
|
|
136
|
-
return response
|
|
137
|
-
elif self.subcommand == 'upload-docs':
|
|
138
|
-
if self.path is None:
|
|
139
|
-
response['msg'] = "Missing required parameter: path"
|
|
140
|
-
return response
|
|
141
|
-
elif self.subcommand == 'publish-version':
|
|
142
|
-
if not self.prefix:
|
|
143
|
-
response['msg'] = "Missing prefix"
|
|
144
|
-
return response
|
|
145
|
-
if not self.version:
|
|
146
|
-
response['msg'] = "Missing version"
|
|
147
|
-
return response
|
|
148
|
-
if not self.http_port:
|
|
149
|
-
response['msg'] = "Missing Ingress HTTP Port"
|
|
150
|
-
return response
|
|
151
|
-
if not self.prefix.startswith('/'):
|
|
152
|
-
response['msg'] = "Prefix should start with a forward slash (/)"
|
|
153
|
-
return response
|
|
154
|
-
if self.prefix.endswith('/'):
|
|
155
|
-
response['msg'] = "Prefix should not end with a forward slash (/)"
|
|
156
|
-
return response
|
|
157
|
-
pattern = r'^v\d+\.\d+\.\d+$'
|
|
158
|
-
if not re.match(pattern, self.version):
|
|
159
|
-
response['msg'] = "Version should be in the format vX.X.X"
|
|
160
|
-
return response
|
|
161
|
-
if not self.http_port.isdigit():
|
|
162
|
-
response['msg'] = "Ingress HTTP Port should be a number"
|
|
163
|
-
return response
|
|
164
|
-
# Send success
|
|
165
|
-
response['error'] = False
|
|
166
|
-
return response
|
|
167
|
-
|
|
168
|
-
def create(self) -> bool:
|
|
169
|
-
"""
|
|
170
|
-
Creating a new snap
|
|
171
|
-
"""
|
|
172
|
-
with Progress(
|
|
173
|
-
SpinnerColumn(),
|
|
174
|
-
TextColumn("[progress.description]{task.description}"),
|
|
175
|
-
transient=True,
|
|
176
|
-
) as progress:
|
|
177
|
-
progress.add_task(description='Creating your snap...', total=None)
|
|
178
|
-
try:
|
|
179
|
-
payload = {
|
|
180
|
-
"service_id": self.sid,
|
|
181
|
-
"name": self.name,
|
|
182
|
-
"description": self.desc,
|
|
183
|
-
"platform": self.platform_type,
|
|
184
|
-
"language": self.language,
|
|
185
|
-
}
|
|
186
|
-
res = requests.post(
|
|
187
|
-
f"{self.base_url}/v1/snapser-api/byosnaps",
|
|
188
|
-
json=payload, headers={'api-key': self.api_key},
|
|
189
|
-
timeout=SERVER_CALL_TIMEOUT
|
|
190
|
-
)
|
|
191
|
-
if res.ok:
|
|
192
|
-
return True
|
|
193
|
-
response_json = res.json()
|
|
194
|
-
info(response_json)
|
|
195
|
-
if "api_error_code" in response_json and "message" in response_json:
|
|
196
|
-
if response_json['api_error_code'] == ERROR_SERVICE_VERSION_EXISTS:
|
|
197
|
-
error(
|
|
198
|
-
'Version already exists. Please update your version and try again'
|
|
199
|
-
)
|
|
200
|
-
elif response_json['api_error_code'] == ERROR_TAG_NOT_AVAILABLE:
|
|
201
|
-
error('Invalid tag. Please use the correct tag')
|
|
202
|
-
elif response_json['api_error_code'] == ERROR_ADD_ON_NOT_ENABLED:
|
|
203
|
-
error(
|
|
204
|
-
'Missing Add-on. Please enable the add-on via the Snapser Web app.'
|
|
205
|
-
)
|
|
206
|
-
else:
|
|
207
|
-
error(f'Server error: {response_json["message"]}')
|
|
208
|
-
else:
|
|
209
|
-
error(
|
|
210
|
-
f'Server error: {json.dumps(response_json, indent=2)}'
|
|
211
|
-
)
|
|
212
|
-
except RequestException as e:
|
|
213
|
-
error(f"Exception: Unable to create your snap {e}")
|
|
214
|
-
return False
|
|
215
|
-
|
|
216
|
-
def build(self) -> bool:
|
|
217
|
-
"""
|
|
218
|
-
Build the Snap image
|
|
219
|
-
"""
|
|
220
|
-
# Get the data
|
|
221
|
-
ecr_repo_url = self.token_parts[0]
|
|
222
|
-
ecr_repo_username = self.token_parts[1]
|
|
223
|
-
ecr_repo_token = self.token_parts[2]
|
|
224
|
-
image_tag = f'{self.sid}.{self.tag}'
|
|
225
|
-
full_ecr_repo_url = f'{ecr_repo_url}:{image_tag}'
|
|
226
|
-
build_platform = ByoSnap.DEFAULT_BUILD_PLATFORM
|
|
227
|
-
if len(self.token_parts) == 4:
|
|
228
|
-
build_platform = self.token_parts[3]
|
|
229
96
|
try:
|
|
230
97
|
# Check dependencies
|
|
231
98
|
with Progress(
|
|
@@ -243,7 +110,19 @@ class ByoSnap:
|
|
|
243
110
|
error('Docker not present')
|
|
244
111
|
return False
|
|
245
112
|
success('Dependencies Verified')
|
|
113
|
+
return True
|
|
114
|
+
except subprocess.CalledProcessError:
|
|
115
|
+
error('Unable to initialize docker')
|
|
116
|
+
return False
|
|
246
117
|
|
|
118
|
+
def _docker_login(self) -> bool:
|
|
119
|
+
"""
|
|
120
|
+
Docker Login
|
|
121
|
+
"""
|
|
122
|
+
ecr_repo_url = self.token_parts[0]
|
|
123
|
+
ecr_repo_username = self.token_parts[1]
|
|
124
|
+
ecr_repo_token = self.token_parts[2]
|
|
125
|
+
try:
|
|
247
126
|
# Login to Snapser Registry
|
|
248
127
|
with Progress(
|
|
249
128
|
SpinnerColumn(),
|
|
@@ -270,7 +149,18 @@ class ByoSnap:
|
|
|
270
149
|
)
|
|
271
150
|
return False
|
|
272
151
|
success('Login Successful')
|
|
152
|
+
return True
|
|
153
|
+
except subprocess.CalledProcessError:
|
|
154
|
+
error('Unable to initialize docker')
|
|
155
|
+
return False
|
|
273
156
|
|
|
157
|
+
def _docker_build(self) -> bool:
|
|
158
|
+
# Get the data
|
|
159
|
+
image_tag = f'{self.sid}.{self.input_tag}'
|
|
160
|
+
build_platform = ByoSnap.DEFAULT_BUILD_PLATFORM
|
|
161
|
+
if len(self.token_parts) == 4:
|
|
162
|
+
build_platform = self.token_parts[3]
|
|
163
|
+
try:
|
|
274
164
|
# Build your snap
|
|
275
165
|
with Progress(
|
|
276
166
|
SpinnerColumn(),
|
|
@@ -295,7 +185,17 @@ class ByoSnap:
|
|
|
295
185
|
error('Unable to build docker')
|
|
296
186
|
return False
|
|
297
187
|
success('Build Successful')
|
|
188
|
+
return True
|
|
189
|
+
except subprocess.CalledProcessError:
|
|
190
|
+
error('CLI Error')
|
|
191
|
+
return False
|
|
298
192
|
|
|
193
|
+
def _docker_tag(self) -> bool:
|
|
194
|
+
# Get the data
|
|
195
|
+
ecr_repo_url = self.token_parts[0]
|
|
196
|
+
image_tag = f'{self.sid}.{self.input_tag}'
|
|
197
|
+
full_ecr_repo_url = f'{ecr_repo_url}:{image_tag}'
|
|
198
|
+
try:
|
|
299
199
|
# Tag the repo
|
|
300
200
|
with Progress(
|
|
301
201
|
SpinnerColumn(),
|
|
@@ -316,18 +216,17 @@ class ByoSnap:
|
|
|
316
216
|
error('Unable to tag your snap')
|
|
317
217
|
return False
|
|
318
218
|
success('Tag Successful')
|
|
319
|
-
|
|
320
219
|
return True
|
|
321
220
|
except subprocess.CalledProcessError:
|
|
322
221
|
error('CLI Error')
|
|
323
222
|
return False
|
|
324
223
|
|
|
325
|
-
def
|
|
224
|
+
def _docker_push(self) -> bool:
|
|
326
225
|
"""
|
|
327
226
|
Push the Snap image
|
|
328
227
|
"""
|
|
329
228
|
ecr_repo_url = self.token_parts[0]
|
|
330
|
-
image_tag = f'{self.sid}.{self.
|
|
229
|
+
image_tag = f'{self.sid}.{self.input_tag}'
|
|
331
230
|
full_ecr_repo_url = f'{ecr_repo_url}:{image_tag}'
|
|
332
231
|
|
|
333
232
|
# Push the image
|
|
@@ -353,6 +252,141 @@ class ByoSnap:
|
|
|
353
252
|
success('Snap Upload Successful')
|
|
354
253
|
return True
|
|
355
254
|
|
|
255
|
+
# Public methods
|
|
256
|
+
|
|
257
|
+
# Validator
|
|
258
|
+
def validate_input(self) -> ResponseType:
|
|
259
|
+
"""
|
|
260
|
+
Validator
|
|
261
|
+
"""
|
|
262
|
+
response: ResponseType = {
|
|
263
|
+
'error': True,
|
|
264
|
+
'msg': '',
|
|
265
|
+
'data': []
|
|
266
|
+
}
|
|
267
|
+
# Check API Key and Base URL
|
|
268
|
+
if not self.api_key or self.base_url == '':
|
|
269
|
+
response['msg'] = "Missing API Key."
|
|
270
|
+
return response
|
|
271
|
+
# Check subcommand
|
|
272
|
+
if not self.subcommand in ByoSnap.SUBCOMMANDS:
|
|
273
|
+
response['msg'] = (
|
|
274
|
+
"Invalid command. Valid commands ",
|
|
275
|
+
f"are {', '.join(ByoSnap.SUBCOMMANDS)}."
|
|
276
|
+
)
|
|
277
|
+
return response
|
|
278
|
+
# Validate the SID
|
|
279
|
+
if not self.sid.startswith(ByoSnap.ID_PREFIX):
|
|
280
|
+
response['msg'] = f"Invalid Snap ID. Valid Snap IDs start with {ByoSnap.ID_PREFIX}."
|
|
281
|
+
return response
|
|
282
|
+
if len(self.sid) > ByoSnap.SID_CHARACTER_LIMIT:
|
|
283
|
+
response['msg'] = (
|
|
284
|
+
"Invalid Snap ID. "
|
|
285
|
+
f"Snap ID should be less than {ByoSnap.SID_CHARACTER_LIMIT} characters"
|
|
286
|
+
)
|
|
287
|
+
return response
|
|
288
|
+
# Validation for subcommands
|
|
289
|
+
if self.subcommand == 'create':
|
|
290
|
+
if self.name == '':
|
|
291
|
+
response['msg'] = "Missing name"
|
|
292
|
+
return response
|
|
293
|
+
if self.language not in ByoSnap.LANGUAGES:
|
|
294
|
+
response['msg'] = (
|
|
295
|
+
"Invalid language. Valid languages are "
|
|
296
|
+
f"{', '.join(ByoSnap.LANGUAGES)}."
|
|
297
|
+
)
|
|
298
|
+
return response
|
|
299
|
+
if self.platform_type not in ByoSnap.PLATFORMS:
|
|
300
|
+
response['msg'] = (
|
|
301
|
+
"Invalid platform. Valid platforms are "
|
|
302
|
+
f"{', '.join(ByoSnap.PLATFORMS)}."
|
|
303
|
+
)
|
|
304
|
+
return response
|
|
305
|
+
else:
|
|
306
|
+
# Check the token
|
|
307
|
+
if self.token_parts is None:
|
|
308
|
+
response['msg'] = 'Invalid token. Please reach out to your support team.'
|
|
309
|
+
return response
|
|
310
|
+
# Check tag
|
|
311
|
+
if self.input_tag is None or len(self.input_tag.split()) > 1 or \
|
|
312
|
+
len(self.input_tag) > ByoSnap.TAG_CHARACTER_LIMIT:
|
|
313
|
+
response['msg'] = (
|
|
314
|
+
"Tag should be a single word with maximum of "
|
|
315
|
+
f"{ByoSnap.TAG_CHARACTER_LIMIT} characters"
|
|
316
|
+
)
|
|
317
|
+
return response
|
|
318
|
+
if self.subcommand == 'build' or self.subcommand == 'publish-image':
|
|
319
|
+
if not self.input_tag:
|
|
320
|
+
response['msg'] = "Missing required parameter: tag"
|
|
321
|
+
return response
|
|
322
|
+
if not self.path:
|
|
323
|
+
response['msg'] = "Missing required parameter: path"
|
|
324
|
+
return response
|
|
325
|
+
# Check path
|
|
326
|
+
if not os.path.isfile(f"{self.path}/{self.dockerfile}"):
|
|
327
|
+
response['msg'] = f"Unable to find {self.dockerfile} at path {self.path}"
|
|
328
|
+
return response
|
|
329
|
+
elif self.subcommand == 'push':
|
|
330
|
+
if not self.input_tag:
|
|
331
|
+
response['msg'] = "Missing required parameter: tag"
|
|
332
|
+
return response
|
|
333
|
+
elif self.subcommand == 'upload-docs':
|
|
334
|
+
if self.path is None:
|
|
335
|
+
response['msg'] = "Missing required parameter: path"
|
|
336
|
+
return response
|
|
337
|
+
elif self.subcommand == 'publish-version':
|
|
338
|
+
if not self.prefix:
|
|
339
|
+
response['msg'] = "Missing prefix"
|
|
340
|
+
return response
|
|
341
|
+
if not self.version:
|
|
342
|
+
response['msg'] = "Missing version"
|
|
343
|
+
return response
|
|
344
|
+
if not self.http_port:
|
|
345
|
+
response['msg'] = "Missing Ingress HTTP Port"
|
|
346
|
+
return response
|
|
347
|
+
if not self.prefix.startswith('/'):
|
|
348
|
+
response['msg'] = "Prefix should start with a forward slash (/)"
|
|
349
|
+
return response
|
|
350
|
+
if self.prefix.endswith('/'):
|
|
351
|
+
response['msg'] = "Prefix should not end with a forward slash (/)"
|
|
352
|
+
return response
|
|
353
|
+
pattern = r'^v\d+\.\d+\.\d+$'
|
|
354
|
+
if not re.match(pattern, self.version):
|
|
355
|
+
response['msg'] = "Version should be in the format vX.X.X"
|
|
356
|
+
return response
|
|
357
|
+
if not self.http_port.isdigit():
|
|
358
|
+
response['msg'] = "Ingress HTTP Port should be a number"
|
|
359
|
+
return response
|
|
360
|
+
# Send success
|
|
361
|
+
response['error'] = False
|
|
362
|
+
return response
|
|
363
|
+
|
|
364
|
+
# CRUD methods
|
|
365
|
+
def build(self) -> bool:
|
|
366
|
+
"""
|
|
367
|
+
Build the image
|
|
368
|
+
1. Check Dependencies
|
|
369
|
+
2. Login to Snapser Registry
|
|
370
|
+
3. Build your snap
|
|
371
|
+
"""
|
|
372
|
+
if not self._check_dependencies() or not self._docker_login() or \
|
|
373
|
+
not self._docker_build():
|
|
374
|
+
return False
|
|
375
|
+
return True
|
|
376
|
+
|
|
377
|
+
def push(self) -> bool:
|
|
378
|
+
"""
|
|
379
|
+
Tag the image
|
|
380
|
+
1. Check Dependencies
|
|
381
|
+
2. Login to Snapser Registry
|
|
382
|
+
3. Tag the snap
|
|
383
|
+
4. Push your snap
|
|
384
|
+
"""
|
|
385
|
+
if not self._check_dependencies() or not self._docker_login() or \
|
|
386
|
+
not self._docker_tag() or not self._docker_push():
|
|
387
|
+
return False
|
|
388
|
+
return True
|
|
389
|
+
|
|
356
390
|
def upload_docs(self) -> bool:
|
|
357
391
|
'''
|
|
358
392
|
Note this step is optional hence we always respond with a True
|
|
@@ -372,7 +406,7 @@ class ByoSnap:
|
|
|
372
406
|
attachment_file = open(swagger_file, "rb")
|
|
373
407
|
url = (
|
|
374
408
|
f"{self.base_url}/v1/snapser-api/byosnaps/"
|
|
375
|
-
f"{self.sid}/docs/{self.
|
|
409
|
+
f"{self.sid}/docs/{self.input_tag}/openapispec"
|
|
376
410
|
)
|
|
377
411
|
test_res = requests.post(
|
|
378
412
|
url, files={"attachment": attachment_file},
|
|
@@ -405,7 +439,7 @@ class ByoSnap:
|
|
|
405
439
|
attachment_file = open(readme_file, "rb")
|
|
406
440
|
url = (
|
|
407
441
|
f"{self.base_url}/v1/snapser-api/byosnaps/"
|
|
408
|
-
f"{self.sid}/docs/{self.
|
|
442
|
+
f"{self.sid}/docs/{self.input_tag}/markdown"
|
|
409
443
|
)
|
|
410
444
|
test_res = requests.post(
|
|
411
445
|
url, files={"attachment": attachment_file},
|
|
@@ -426,11 +460,68 @@ class ByoSnap:
|
|
|
426
460
|
)
|
|
427
461
|
return True
|
|
428
462
|
|
|
463
|
+
# Upper echelon commands
|
|
464
|
+
def create(self) -> bool:
|
|
465
|
+
"""
|
|
466
|
+
Creating a new snap
|
|
467
|
+
"""
|
|
468
|
+
with Progress(
|
|
469
|
+
SpinnerColumn(),
|
|
470
|
+
TextColumn("[progress.description]{task.description}"),
|
|
471
|
+
transient=True,
|
|
472
|
+
) as progress:
|
|
473
|
+
progress.add_task(description='Creating your snap...', total=None)
|
|
474
|
+
try:
|
|
475
|
+
payload = {
|
|
476
|
+
"service_id": self.sid,
|
|
477
|
+
"name": self.name,
|
|
478
|
+
"description": self.desc,
|
|
479
|
+
"platform": self.platform_type,
|
|
480
|
+
"language": self.language,
|
|
481
|
+
}
|
|
482
|
+
res = requests.post(
|
|
483
|
+
f"{self.base_url}/v1/snapser-api/byosnaps",
|
|
484
|
+
json=payload, headers={'api-key': self.api_key},
|
|
485
|
+
timeout=SERVER_CALL_TIMEOUT
|
|
486
|
+
)
|
|
487
|
+
if res.ok:
|
|
488
|
+
return True
|
|
489
|
+
response_json = res.json()
|
|
490
|
+
info(response_json)
|
|
491
|
+
if "api_error_code" in response_json and "message" in response_json:
|
|
492
|
+
if response_json['api_error_code'] == ERROR_SERVICE_VERSION_EXISTS:
|
|
493
|
+
error(
|
|
494
|
+
'Version already exists. Please update your version and try again'
|
|
495
|
+
)
|
|
496
|
+
elif response_json['api_error_code'] == ERROR_TAG_NOT_AVAILABLE:
|
|
497
|
+
error('Invalid tag. Please use the correct tag')
|
|
498
|
+
elif response_json['api_error_code'] == ERROR_ADD_ON_NOT_ENABLED:
|
|
499
|
+
error(
|
|
500
|
+
'Missing Add-on. Please enable the add-on via the Snapser Web app.'
|
|
501
|
+
)
|
|
502
|
+
else:
|
|
503
|
+
error(f'Server error: {response_json["message"]}')
|
|
504
|
+
else:
|
|
505
|
+
error(
|
|
506
|
+
f'Server error: {json.dumps(response_json, indent=2)}'
|
|
507
|
+
)
|
|
508
|
+
except RequestException as e:
|
|
509
|
+
error(f"Exception: Unable to create your snap {e}")
|
|
510
|
+
return False
|
|
511
|
+
|
|
429
512
|
def publish_image(self) -> bool:
|
|
430
513
|
"""
|
|
431
514
|
Publish the image
|
|
515
|
+
1. Check Dependencies
|
|
516
|
+
2. Login to Snapser Registry
|
|
517
|
+
3. Build your snap
|
|
518
|
+
4. Tag the repo
|
|
519
|
+
5. Push the image
|
|
520
|
+
6. Upload swagger.json
|
|
432
521
|
"""
|
|
433
|
-
if not self.
|
|
522
|
+
if not self._check_dependencies() or not self._docker_login() or \
|
|
523
|
+
not self._docker_build() or not self._docker_tag() or not self._docker_push() or \
|
|
524
|
+
not self.upload_docs():
|
|
434
525
|
return False
|
|
435
526
|
return True
|
|
436
527
|
|
|
@@ -448,7 +539,7 @@ class ByoSnap:
|
|
|
448
539
|
try:
|
|
449
540
|
payload = {
|
|
450
541
|
"version": self.version,
|
|
451
|
-
"image_tag": self.
|
|
542
|
+
"image_tag": self.input_tag,
|
|
452
543
|
"base_url": f"{self.prefix}/{self.sid}",
|
|
453
544
|
"http_port": self.http_port,
|
|
454
545
|
}
|
snapctl/commands/snapend.py
CHANGED
|
@@ -30,7 +30,7 @@ class Snapend:
|
|
|
30
30
|
MAX_BLOCKING_RETRIES = 24
|
|
31
31
|
|
|
32
32
|
def __init__(
|
|
33
|
-
self, subcommand: str, base_url: str, api_key: str, snapend_id: str, category: str,
|
|
33
|
+
self, subcommand: str, base_url: str, api_key: str | None, snapend_id: str, category: str,
|
|
34
34
|
platform_type: str, auth_type: str, path: Union[str, None], snaps: Union[str, None],
|
|
35
35
|
byosnaps: Union[str, None], byogs: Union[str, None], blocking: bool = False
|
|
36
36
|
) -> None:
|
|
@@ -131,6 +131,10 @@ class Snapend:
|
|
|
131
131
|
'msg': '',
|
|
132
132
|
'data': []
|
|
133
133
|
}
|
|
134
|
+
# Check API Key and Base URL
|
|
135
|
+
if not self.api_key or self.base_url == '':
|
|
136
|
+
response['msg'] = "Missing API Key."
|
|
137
|
+
return response
|
|
134
138
|
# Check subcommand
|
|
135
139
|
if not self.subcommand in Snapend.SUBCOMMANDS:
|
|
136
140
|
response['msg'] = \
|