suite-py 1.41.2__py3-none-any.whl → 1.41.4__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.
Files changed (38) hide show
  1. suite_py/__version__.py +1 -1
  2. suite_py/cli.py +107 -185
  3. suite_py/commands/aggregator.py +3 -5
  4. suite_py/commands/ask_review.py +3 -5
  5. suite_py/commands/batch_job.py +1 -2
  6. suite_py/commands/bump.py +1 -2
  7. suite_py/commands/check.py +3 -5
  8. suite_py/commands/context.py +26 -0
  9. suite_py/commands/create_branch.py +1 -2
  10. suite_py/commands/deploy.py +3 -5
  11. suite_py/commands/docker.py +1 -2
  12. suite_py/commands/generator.py +1 -2
  13. suite_py/commands/id.py +1 -2
  14. suite_py/commands/ip.py +1 -2
  15. suite_py/commands/login.py +4 -173
  16. suite_py/commands/merge_pr.py +3 -4
  17. suite_py/commands/open_pr.py +4 -5
  18. suite_py/commands/project_lock.py +3 -5
  19. suite_py/commands/release.py +3 -5
  20. suite_py/commands/secret.py +1 -2
  21. suite_py/commands/set_token.py +1 -2
  22. suite_py/commands/status.py +3 -4
  23. suite_py/lib/config.py +1 -3
  24. suite_py/lib/handler/captainhook_handler.py +44 -54
  25. suite_py/lib/handler/metrics_handler.py +8 -6
  26. suite_py/lib/handler/okta_handler.py +81 -0
  27. suite_py/lib/logger.py +1 -0
  28. suite_py/lib/metrics.py +4 -2
  29. suite_py/lib/oauth.py +156 -0
  30. suite_py/lib/tokens.py +4 -0
  31. {suite_py-1.41.2.dist-info → suite_py-1.41.4.dist-info}/METADATA +2 -4
  32. suite_py-1.41.4.dist-info/RECORD +54 -0
  33. suite_py/commands/qa.py +0 -424
  34. suite_py/lib/handler/qainit_handler.py +0 -259
  35. suite_py-1.41.2.dist-info/RECORD +0 -53
  36. /suite_py/{commands/templates → templates}/login.html +0 -0
  37. {suite_py-1.41.2.dist-info → suite_py-1.41.4.dist-info}/WHEEL +0 -0
  38. {suite_py-1.41.2.dist-info → suite_py-1.41.4.dist-info}/entry_points.txt +0 -0
suite_py/commands/qa.py DELETED
@@ -1,424 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
-
3
- import copy
4
- import datetime
5
- import json
6
- import re
7
- import sys
8
-
9
- from dateutil import parser, tz
10
- from rich.console import Console
11
- from rich.table import Table
12
-
13
- from suite_py.commands.login import Login
14
- from suite_py.lib import logger
15
- from suite_py.lib import metrics
16
- from suite_py.lib.handler import git_handler as git
17
- from suite_py.lib.handler import prompt_utils
18
- from suite_py.lib.handler.drone_handler import DroneHandler
19
- from suite_py.lib.handler.git_handler import GitHandler
20
- from suite_py.lib.handler.qainit_handler import QainitHandler
21
- from suite_py.lib.handler.youtrack_handler import YoutrackHandler
22
-
23
-
24
- class QA:
25
- def __init__(self, action, project, config, tokens, flags=None):
26
- self._action = action
27
- self._project = project
28
- self._flags = flags
29
- self._config = config
30
- self._tokens = tokens
31
- self._git = GitHandler(project, config)
32
- self._qainit = QainitHandler(project, config, tokens)
33
- self._youtrack = YoutrackHandler(config, tokens)
34
- self._drone = DroneHandler(config, tokens)
35
-
36
- @metrics.command("qa")
37
- def run(self):
38
- if not self._qainit.user_info():
39
- logger.warning("You're not logged in.")
40
- Login(self._config).run()
41
-
42
- if self._action == "list":
43
- self._list()
44
- elif self._action == "create":
45
- self._create()
46
- elif self._action == "update":
47
- self._update()
48
- elif self._action == "delete":
49
- self._delete()
50
- elif self._action == "freeze":
51
- self._freeze()
52
- elif self._action == "unfreeze":
53
- self._unfreeze()
54
- elif self._action == "check":
55
- self._check()
56
- elif self._action == "describe":
57
- self._describe()
58
- elif self._action == "update-quota":
59
- self._update_quota()
60
- elif self._action == "toggle-maintenance":
61
- self._toggle_maintenance()
62
-
63
- def _check(self):
64
- logger.info(
65
- "Checking configuration. If there is an issue, check ~/.suite_py/config.yml file and execute: suite-py login"
66
- )
67
-
68
- self._qainit.user_info()
69
-
70
- def _clean_date(self, datetime_str):
71
- # expected format: '2021-07-23T14:04:12.000000Z'
72
- datetime_object = datetime.datetime.strptime(
73
- datetime_str, "%Y-%m-%dT%H:%M:%S.%fZ"
74
- )
75
- # Define time zones:
76
- utc_time_zone = tz.tzutc()
77
- local_time_zone = tz.tzlocal()
78
- # Convert time zone
79
- utc_datetime_object = datetime_object.replace(tzinfo=utc_time_zone)
80
- local_datetime_object = utc_datetime_object.astimezone(local_time_zone)
81
- return local_datetime_object.strftime("%d/%m/%Y %H:%M:%S %z")
82
-
83
- def _create_instance_table(self):
84
- instance_table = Table()
85
- instance_table.add_column("Name", style="purple")
86
- instance_table.add_column("Hash", style="green", width=32)
87
- instance_table.add_column("Card", style="white")
88
- instance_table.add_column("Created by", style="white")
89
- instance_table.add_column("Updated by", style="white")
90
- instance_table.add_column("Deleted by", style="white")
91
- instance_table.add_column("Last update", style="white")
92
- instance_table.add_column("Status", style="white")
93
-
94
- return instance_table
95
-
96
- def _insert_instance_record(self, table, qa):
97
- table.add_row(
98
- qa["name"],
99
- qa["hash"],
100
- qa["card"],
101
- (
102
- qa.get("created", {}).get("github_username", "/")
103
- if qa["created"] is not None
104
- else "/"
105
- ),
106
- (
107
- qa.get("updated", {}).get("github_username", "/")
108
- if qa["updated"] is not None
109
- else "/"
110
- ),
111
- (
112
- qa.get("deleted", {}).get("github_username", "/")
113
- if qa["deleted"] is not None
114
- else "/"
115
- ),
116
- self._clean_date(qa["updated_at"]),
117
- qa["status"],
118
- )
119
-
120
- def _list(self):
121
- # init empty table with column (useful for reset)
122
- empty_table = self._create_instance_table()
123
- table = copy.deepcopy(empty_table)
124
- console = Console()
125
-
126
- # execute query with pagination and filtering
127
- page_number = 1
128
- while True:
129
- r = self._qainit.list(
130
- self._flags,
131
- page=page_number,
132
- page_size=self._config.qainit["table_size"],
133
- )
134
- response = r.json()
135
- qa_list = response["list"]
136
- for qa in qa_list:
137
- self._insert_instance_record(table, qa)
138
-
139
- console.print(table)
140
-
141
- # break conditions
142
- if response["page_number"] >= response["total_pages"]:
143
- break
144
- if not prompt_utils.ask_confirm(
145
- f"I found {response['total_entries']} results. Do you want to load a few more?",
146
- False,
147
- ):
148
- break
149
- page_number += 1
150
- # table reset
151
- table = copy.deepcopy(empty_table)
152
-
153
- def _describe(self):
154
- qa_hash = self._flags["hash"]
155
- jsonify = self._flags["json"]
156
-
157
- r = self._qainit.describe(qa_hash)
158
-
159
- issues = self._check_instance_issues(r)
160
- if issues:
161
- msg = "an issue" if len(issues) == 1 else f"{len(issues)} issues"
162
- logger.warning(f"⚠️ suite-py diagnostic tool found {msg}:")
163
- for i in issues:
164
- logger.warning(i)
165
- logger.warning(
166
- "If the problem persist write a detailed message on #team-platform-operations"
167
- )
168
- sys.exit(-1)
169
-
170
- if jsonify:
171
- print(json.dumps(r, sort_keys=True, indent=2))
172
- else:
173
- # RESOURCES TABLE
174
- table = Table()
175
- table.add_column("Microservice", style="purple", no_wrap=True)
176
- table.add_column("Drone build")
177
- table.add_column("Branch", style="white")
178
- table.add_column("Last update", style="white")
179
- table.add_column("Status", style="white")
180
-
181
- # INSTANCE TABLE
182
- instance_table = self._create_instance_table()
183
-
184
- self._insert_instance_record(instance_table, r["list"])
185
-
186
- # DNS TABLE
187
- dns_table = Table()
188
- dns_table.add_column("Name", style="purple", no_wrap=True)
189
- dns_table.add_column("Record", style="green")
190
-
191
- console = Console()
192
-
193
- try:
194
- resources_list = sorted(r["list"]["resources"], key=lambda k: k["name"])
195
- for resource in resources_list:
196
- if (
197
- (
198
- resource["type"] == "microservice"
199
- or "service" in resource["name"]
200
- )
201
- and "dns" in resource
202
- and resource["dns"] is not None
203
- ):
204
- for key, value in resource["dns"].items():
205
- dns_table.add_row(key, value)
206
- if resource["type"] == "microservice":
207
- drone_url = (
208
- (
209
- "[blue][u]"
210
- + "https://drone-1.prima.it/primait/"
211
- + resource["name"]
212
- + "/"
213
- + resource["promoted_build"]
214
- + "[/u][/blue]"
215
- )
216
- if resource["promoted_build"]
217
- else "Not available"
218
- )
219
- table.add_row(
220
- resource["name"],
221
- drone_url,
222
- (
223
- resource["ref"]
224
- if resource["ref"] == "master"
225
- else f"[green]{resource['ref']}[/green]"
226
- ),
227
- self._clean_date(resource["updated_at"]),
228
- resource["status"],
229
- )
230
-
231
- console.print(instance_table)
232
- console.print(dns_table)
233
- console.print(table)
234
- except TypeError as e:
235
- logger.error(f"Unexpected response format: {e}")
236
-
237
- def _delete(self):
238
- qa_hashes = self._flags["hashes"]
239
- force = self._flags["force"]
240
- for qa_hash in qa_hashes:
241
- if force:
242
- if not self._qainit.force_update(qa_hash):
243
- # this function fails only if the QA is effectively stuck
244
- # and qa init it's unable to solve it
245
- logger.error(
246
- "QA force deletion has failed, please contact #platform-operations team."
247
- )
248
- return
249
- self._qainit.delete(qa_hash, force)
250
-
251
- def _freeze(self):
252
- self._qainit.freeze(self._flags["hash"])
253
-
254
- def _unfreeze(self):
255
- self._qainit.unfreeze(self._flags["hash"])
256
-
257
- def _create(self):
258
- user = self._qainit.user_info()
259
-
260
- if not user["quota"]["remaining"] > 0:
261
- logger.error("There's no remaining quota for you.")
262
- sys.exit("-1")
263
-
264
- if "staging" in self._qainit.url:
265
- qa_default_name = (
266
- f"staging_{git.get_username()}_{self._git.current_branch_name()}"
267
- )
268
- else:
269
- qa_default_name = f"{git.get_username()}_{self._git.current_branch_name()}"
270
-
271
- qa_name = prompt_utils.ask_questions_input(
272
- "Choose the QA name: ", default_text=qa_default_name
273
- )
274
-
275
- card_match = re.match(r"[^\/]*_(?P<name>[A-Z]+-\d+)\/", qa_name)
276
- default_card_name = (
277
- card_match["name"] if card_match else self._config.user["default_slug"]
278
- )
279
-
280
- qa_card = prompt_utils.ask_questions_input(
281
- "Youtrack issue ID: ", default_text=default_card_name
282
- )
283
-
284
- if qa_card != "":
285
- try:
286
- self._youtrack.get_issue(qa_card)
287
- except Exception:
288
- logger.error("invalid Youtrack issue ID")
289
- sys.exit(-1)
290
-
291
- self._qainit.create(qa_name, qa_card, self._flags["services"])
292
-
293
- def _update(self):
294
- self._qainit.update(self._flags["hash"], self._flags["services"])
295
-
296
- def _update_quota(self):
297
- username = prompt_utils.ask_questions_input("Insert GitHub username: ")
298
- quota = prompt_utils.ask_questions_input("Insert new quota value: ")
299
-
300
- self._qainit.update_user_quota(username, quota)
301
-
302
- def _toggle_maintenance(self):
303
- self._qainit.maintenance()
304
-
305
- def _check_instance_issues(self, response):
306
- issues = []
307
-
308
- if not response["list"]:
309
- issues.append("QA not found")
310
-
311
- # Check if instance status is still pending
312
- if response["list"] and response["list"]["status"] in ["creating", "updating"]:
313
- microservices = self._filter_list(
314
- "type", ["microservice"], response["list"]["resources"]
315
- )
316
- # 1. Microservices list is empty?
317
- if len(microservices) == 0:
318
- issues.append("Microservices list is empty, please be patient.")
319
- return issues
320
-
321
- # 2. Check if qainit worker is still launching updates
322
- if not self._is_update_initiated(microservices, response["list"]):
323
- issues.append(
324
- "Qainit is still working on microservices, try again in a while."
325
- )
326
- issues.append("https://www.youtube.com/watch?v=dQw4w9WgXcQ")
327
- return issues
328
-
329
- # 3. Check every resource
330
- stale_resources = self._filter_list(
331
- "status", ["creating", "updating"], microservices
332
- )
333
- for resource in stale_resources:
334
- issues += self._check_resource_issues(resource)
335
-
336
- # 4. Check is instance is stuck in a stale status
337
- if (
338
- len(microservices) > 0
339
- and len(stale_resources) == 0
340
- and self._minutes_elapsed_since_update(response["list"]) >= 5
341
- ):
342
- logger.warning("Trying to call qainit to force QA update...")
343
- if self._qainit.force_update(response["list"]["hash"]):
344
- issues.append(
345
- "Your QA was in a stale status and has been fixed, launch `suite-py describe` again please."
346
- )
347
- else:
348
- issues.append(
349
- "Your QA was in a stale status but forced update has failed, please contact #platform-operations team."
350
- )
351
-
352
- return issues
353
-
354
- def _is_update_initiated(self, microservices, instance):
355
- microservices_updates = [
356
- parser.parse(x["updated_at"]).timestamp() for x in microservices
357
- ]
358
- max_microservice_update = max(microservices_updates)
359
- instance_update = parser.parse(instance["updated_at"]).timestamp()
360
-
361
- return max_microservice_update >= instance_update
362
-
363
- def _minutes_elapsed_since_update(self, instance):
364
- return (
365
- datetime.datetime.now().timestamp()
366
- - parser.parse(instance["updated_at"]).timestamp()
367
- ) / 60
368
-
369
- def _check_resource_issues(self, resource):
370
- issues = []
371
-
372
- build = self._drone.get_repo_build(resource["name"], resource["promoted_build"])
373
-
374
- if "stages" not in build:
375
- issues.append("Suite-py is unable to locate drone build.")
376
- return issues
377
-
378
- qainit_step = self._filter_list("name", ["qainit-it"], build["stages"])[0]
379
- # Check if build is succeded
380
- if build["status"] == "success" or qainit_step["status"] == "error":
381
- issues.append(
382
- f"Something between Drone and Qainit went wrong, did you try to restart the build? {self._drone.get_repo_build_url(resource['name'], resource['promoted_build'])}"
383
- )
384
-
385
- # Check if build is killed
386
- elif build["status"] == "killed":
387
- issues.append(
388
- f"Seems someone killed the build, did you try to restart the build? {self._drone.get_repo_build_url(resource['name'], resource['promoted_build'])}"
389
- )
390
-
391
- # Check if build is failed
392
- elif build["status"] == "failure":
393
- build_pipeline = self._filter_list("name", ["build_qa"], build["stages"])[0]
394
- if build_pipeline and build_pipeline["status"] == "failed":
395
- issues.append(
396
- f"Something went wrong during microservice build step, check the logs {self._drone.get_build_and_pipeline_url(resource['name'], resource['promoted_build'], build_pipeline['number'])}"
397
- )
398
-
399
- deploy_pipeline = self._filter_list(
400
- "name", ["deploy-it-qa"], build["stages"]
401
- )[0]
402
- if deploy_pipeline and deploy_pipeline["status"] == "failed":
403
- issues.append(
404
- f"Something went wront during microservice deploy step, check the logs {self._drone.get_build_and_pipeline_url(resource['name'], resource['promoted_build'], deploy_pipeline['number'])}"
405
- )
406
- # Check if build is running for over an hour
407
- elif (
408
- build["status"] == "running"
409
- and self._difference_in_hours(build["started"]) >= 1
410
- ):
411
- issues.append(
412
- f"It looks like the build is stuck, did you try to restart the build? {self._drone.get_repo_build_url(resource['name'], resource['promoted_build'])}"
413
- )
414
-
415
- return issues
416
-
417
- def _filter_list(self, search_key, in_list, search_list):
418
- return list(filter(lambda l: l[search_key] in in_list, search_list))
419
-
420
- def _difference_in_hours(self, unix_timestamp):
421
- ts = datetime.datetime.fromtimestamp(unix_timestamp)
422
- current_ts = datetime.datetime.utcnow()
423
-
424
- return (current_ts - ts).total_seconds() / 60 / 60
@@ -1,259 +0,0 @@
1
- # -*- encoding: utf-8 -*-
2
- import json
3
- import sys
4
-
5
- import requests
6
- from github.GithubException import UnknownObjectException
7
- from halo import Halo
8
-
9
- from suite_py.lib import logger
10
- from suite_py.lib.handler import prompt_utils
11
- from suite_py.lib.handler.git_handler import GitHandler
12
- from suite_py.lib.handler.github_handler import GithubHandler
13
-
14
-
15
- class QainitHandler:
16
-
17
- scope_mapping = {
18
- "admin": [
19
- "update:user-quota",
20
- "delete:others-qa",
21
- "create:qa",
22
- "delete:qa",
23
- "describe:qa",
24
- "describe:others-qa",
25
- "update:qa",
26
- "update:others-qa",
27
- "list:qa",
28
- "list:others-qa",
29
- "access:captainhook",
30
- ],
31
- "dev": [
32
- "create:qa",
33
- "delete:qa",
34
- "describe:qa",
35
- "describe:others-qa",
36
- "update:qa",
37
- "update:others-qa",
38
- "list:qa",
39
- "list:others-qa",
40
- "access:captainhook",
41
- ],
42
- "external": ["create:qa", "delete:qa", "describe:qa", "list:qa", "update:qa"],
43
- }
44
-
45
- def __init__(self, project, config, tokens):
46
- self._project = project
47
- self._token = tokens.drone
48
- self._config = config
49
- self.url = self._config.qainit["url"]
50
- self.okta_tenant = self._config.okta["tenant"]
51
- self.okta_token = self._config.get_cache(f"{self.okta_tenant}_okta_token")
52
- self._github = GithubHandler(tokens)
53
- self._git = GitHandler(project, config)
54
-
55
- if "url" not in config.qainit:
56
- self.usage()
57
- sys.exit(-1)
58
-
59
- def usage(self):
60
- logger.warning(
61
- "Unable to use QA commands: missing qainit config in ~/.suite_py/config.yml"
62
- )
63
- logger.warning(
64
- "Update your config.yml as: https://github.com/primait/suite_py/blob/master/.config.yml.dist"
65
- )
66
-
67
- def user_info(self):
68
- r = self._execute("GET", "/api/v1/user")
69
-
70
- if r.status_code == 401:
71
- return None
72
-
73
- logger.debug(json.dumps(r.json(), indent=2))
74
-
75
- return r.json()
76
-
77
- def update_user_quota(self, username, quota):
78
- body = {"github_username": f"{username}", "quota": f"{quota}"}
79
- logger.debug(json.dumps(body))
80
- r = self._execute(
81
- "POST",
82
- "/api/v1/user/quota",
83
- body=json.dumps(body),
84
- )
85
-
86
- logger.info("Quota updated.")
87
- logger.debug(json.dumps(r.json(), indent=2))
88
-
89
- def create(self, name, card, services):
90
- srv_list = self.create_services_body(services)
91
- body = {"name": name, "card": card, "services": srv_list}
92
-
93
- logger.debug(json.dumps(body))
94
- r = self._execute(
95
- "POST",
96
- "/api/v1/qa",
97
- body=json.dumps(body),
98
- )
99
-
100
- logger.info(f"QA creation initiated. Your namespace hash: {r.json()['hash']}")
101
- logger.debug(json.dumps(r.json(), indent=2))
102
-
103
- def update(self, qa_hash, services):
104
- srv_list = self.create_services_body(services)
105
-
106
- body = {"services": srv_list}
107
- logger.debug(json.dumps(body))
108
-
109
- r = self._execute(
110
- "PUT",
111
- f"/api/v1/qa/{qa_hash}",
112
- body=json.dumps(body),
113
- )
114
- self.handle_call_result(r, "update", qa_hash)
115
-
116
- def list(self, params, page=1, page_size=10):
117
- filters = []
118
- status_values = '"created","creating","updated","updating","failed","frozen","freezing","unfreezing"'
119
- if len(params["status"]) > 0:
120
- status_values = ",".join([f'"{status}"' for status in params["status"]])
121
- filters.append(f"status=[{status_values}]")
122
-
123
- if params["user"] is not None:
124
- filters.append(f"user={params['user']}")
125
-
126
- if params["card"] is not None:
127
- filters.append(f"card={params['card']}")
128
-
129
- filters_string = "&".join(filters)
130
-
131
- return self._execute(
132
- "GET",
133
- f"/api/v1/qa?{filters_string}&page_size={page_size}&page={page}",
134
- )
135
-
136
- def delete(self, qa_hash, force=False):
137
- r = self._execute(
138
- "DELETE",
139
- f"/api/v1/qa/{qa_hash}",
140
- )
141
- logger.debug(json.dumps(r.json(), indent=2))
142
- if r.ok:
143
- logger.info(f"QA {qa_hash} deletion initiated")
144
- else:
145
- logger.error(
146
- f"QA {qa_hash} deletion has failed{', try running this command with --force flag' if not force else ''}."
147
- )
148
-
149
- def describe(self, qa_hash):
150
- r = self._execute(
151
- "GET",
152
- f"/api/v1/qa/{qa_hash}",
153
- ).json()
154
- logger.debug(json.dumps(r, indent=2))
155
-
156
- return r
157
-
158
- def create_services_body(self, prj_list):
159
- srv_list = []
160
- ref = self._git.current_branch_name()
161
- for prj in prj_list:
162
- with Halo(text="Loading branches...", spinner="dots", color="magenta"):
163
- choices = [
164
- {"name": branch.name, "value": branch.name}
165
- for branch in self._github.get_branches(prj)
166
- ]
167
- if choices:
168
- choices.sort(key=lambda x: x["name"])
169
- ref = prompt_utils.ask_choices(
170
- f"Select branch for project - {prj}: ", choices, default_text=ref
171
- )
172
- try:
173
- self._github.get_raw_content(prj, ref, ".service.yml")
174
- except UnknownObjectException:
175
- logger.error(
176
- f".service.yml missing for project: {prj}, can't add microservice to QA"
177
- )
178
- sys.exit(-1)
179
- srv_list.append(
180
- {
181
- "name": prj,
182
- "ref": ref,
183
- }
184
- )
185
-
186
- return srv_list
187
-
188
- def freeze(self, qa_hash):
189
- body = {"operation": "freeze"}
190
- logger.debug(json.dumps(body))
191
- r = self._execute("PUT", f"/api/v1/qa/{qa_hash}", body=json.dumps(body))
192
- self.handle_call_result(r, "freezing", qa_hash)
193
-
194
- def unfreeze(self, qa_hash):
195
- body = {"operation": "unfreeze"}
196
- logger.debug(json.dumps(body))
197
- r = self._execute("PUT", f"/api/v1/qa/{qa_hash}", body=json.dumps(body))
198
- self.handle_call_result(r, "unfreezing", qa_hash)
199
-
200
- def maintenance(self):
201
- r = self._execute("POST", "/api/v1/maintenance")
202
- if r.json()["success"]:
203
- maintenance = r.json()["maintenance"]
204
- logger.info(
205
- f"Maintenance mode is now {'enabled' if maintenance else 'disabled'}"
206
- )
207
-
208
- logger.debug(json.dumps(r.json(), indent=2))
209
-
210
- def force_update(self, qa_hash):
211
- r = self._execute("POST", f"/api/v1/qa/shit-shoveler/force-update/{qa_hash}")
212
- if r.status_code == 400:
213
- msg = r.json()["message"]
214
- if msg == "instance_not_force_updated":
215
- logger.error("Failed to force update QA.")
216
- return False
217
- if msg == "not_in_stale_state":
218
- logger.warning("QA is not in a stale state.")
219
- return True
220
- raise ValueError(
221
- "Unexpected return type from qa init shit-shoveler endpoint."
222
- )
223
- if r.status_code == 500:
224
- logger.error("Something went wrong")
225
- return False
226
- logger.info("QA has been forced to update")
227
- logger.debug(json.dumps(r.json(), indent=2))
228
- return True
229
-
230
- def _execute(self, request_method, api_endpoint, body=None):
231
- api_url = self.url + api_endpoint
232
- okta_token = self._config.get_cache(f"{self.okta_tenant}_okta_token")
233
-
234
- headers = {
235
- "Content-Type": "application/json",
236
- "Authorization": f"Bearer {okta_token}",
237
- }
238
- logger.debug(request_method)
239
- logger.debug(api_url)
240
- logger.debug(headers)
241
- logger.debug(body)
242
- # pylint: disable-next=missing-timeout
243
- r = requests.request(request_method, api_url, headers=headers, data=body)
244
-
245
- if r.ok:
246
- logger.debug("Call to qainit-evo executed successfully")
247
- else:
248
- logger.error("Some issue during call to qainit-evo: ")
249
- logger.error(f"Status code: {r.status_code}, response: {r.text}")
250
- logger.debug(api_endpoint)
251
-
252
- return r
253
-
254
- def handle_call_result(self, req, operation, qa_hash):
255
- if req.ok:
256
- logger.info(f"QA {operation} initiated")
257
- else:
258
- logger.debug(json.dumps(req.json(), indent=2))
259
- logger.info(f"Run suite-py qa describe {qa_hash} to check the status")