apihandler.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. #! /usr/bin/env python
  2. # -*- coding: utf8 -*-
  3. #
  4. # gitlab irc sender
  5. # Copyright (C) 2016-2017 Andrei Karas (4144)
  6. #
  7. # This program is free software; you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation; either version 3 of the License, or
  10. # any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. import requests
  20. import threading
  21. import time
  22. from code.configuration import Configuration
  23. from code.logger import Logger
  24. class ApiHandler(threading.Thread):
  25. def __init__(self):
  26. threading.Thread.__init__(self)
  27. self.die = False
  28. self.requests = []
  29. self.retry_builds = dict()
  30. def run(self):
  31. while (self.die == False):
  32. while len(self.requests) > 0:
  33. data = self.requests.pop()
  34. if len(data) < 2:
  35. continue
  36. self.parse_command(data);
  37. time.sleep(Configuration.api_sleep_time)
  38. def add(self, data):
  39. self.requests.insert(0, data)
  40. def send_request(self, url, token, flag, cnt):
  41. cnt = cnt + 1
  42. while cnt > 0:
  43. Logger.addLog("Send get api request: {0}, retry count: {1}".format(url, cnt))
  44. cnt = cnt - 1
  45. try:
  46. if flag == True:
  47. func = requests.post
  48. else:
  49. func = requests.get
  50. request = func(url,
  51. headers = {
  52. "PRIVATE-TOKEN": token,
  53. "Accept": "application/vnd.gitlab.v4+json"
  54. }
  55. );
  56. request.raise_for_status()
  57. Logger.addLog("Request status: {0}".format(request.status_code))
  58. if request.status_code >= 200 and request.status_code <= 210:
  59. return request
  60. except Exception as e:
  61. Logger.addError("Request exception: {0}".format(e))
  62. Logger.addError("Retry gitlab api request: " + url)
  63. time.sleep(Configuration.api_sleep_time)
  64. return None
  65. def parse_command(self, data):
  66. command = data[0]
  67. options = data[1]
  68. if command == "getlog":
  69. project_id = data[2]
  70. build_id = data[3]
  71. sha = data[4]
  72. request = self.request_build_log(project_id, build_id, options)
  73. if request is None:
  74. Logger.addError("Api request failed")
  75. return
  76. if len(request.text) == 0:
  77. Logger.addLog("Gitlab stalled build issue detected: {0}, sha: {1}".format(build_id, sha))
  78. request = self.request_build_retry(project_id, build_id, sha, options)
  79. elif request.text.find("ERROR: Job failed (system failure):") >= 0 or \
  80. request.text.find("ERROR: Build failed (system failure):") >= 0 or \
  81. request.text.find("\nFATAL: invalid argument  \nERROR: Build failed: exit code 1\n") > 0 or \
  82. request.text.find("\nFATAL: invalid argument  \nERROR: Job failed: exit code 1\n") > 0 or \
  83. request.text.find("ERROR: Job failed: canceled\n") > 0:
  84. Logger.addLog("Gitlab build issue detected. Retry build: {0}, sha: {1}".format(build_id, sha))
  85. request = self.request_build_retry(project_id, build_id, sha, options)
  86. elif (request.text.find("Using Shell executor...\nNo passwd entry for user 'gitlab-runner'\n") >= 0 or \
  87. request.text.find("ERROR: Job failed: exit code 137\n") >= 0):
  88. Logger.addLog("Gitlab runner issue detected. Retry build: {0}, sha: {1}".format(build_id, sha))
  89. request = self.request_build_retry(project_id, build_id, sha, options)
  90. elif request.text.find("remote: GitLab is not responding\nfatal: unable to access 'https://gitlab-ci-token:xxxxxxxxxxxxxxxxxxxx@gitlab.com/") >= 0:
  91. Logger.addLog("Gitlab runner issue detected. Retry build: {0}, sha: {1}".format(build_id, sha))
  92. request = self.request_build_retry(project_id, build_id, sha, options)
  93. elif request.text.find("ERROR: Job failed") < 0:
  94. Logger.addLog("Gitlab runner issue detected. Retry build: {0}, sha: {1}".format(build_id, sha))
  95. request = self.request_build_retry(project_id, build_id, sha, options)
  96. elif request.text.find("fatal: unable to access 'https://gitlab-ci-token:xxxxxxxxxxxxxxxxxxxx@") > 0 and \
  97. request.text.find(".git/': The requested URL returned error: 500\n/bin/bash: line ") > 0:
  98. Logger.addLog("Gitlab cloning issue detected. Retry build: {0}, sha: {1}".format(build_id, sha))
  99. request = self.request_build_retry(project_id, build_id, sha, options)
  100. elif request.text.find("ERROR: Uploading artifacts to coordinator... error error=couldn't execute POST against http") >= 0 and \
  101. (request.text.find("FATAL: invalid argument  \nERROR: Job failed: exit code 1") > 0 or \
  102. request.text.find("FATAL: invalid argument  \nERROR: Job failed: exit status 1")) > 0:
  103. Logger.addLog("Gitlab artifacts upload issue detected. Retry build: {0}, sha: {1}".format(build_id, sha))
  104. request = self.request_build_retry(project_id, build_id, sha, options)
  105. elif request.text.find("ERROR: Job failed (system failure): Error: No such image: ") >= 0:
  106. Logger.addLog("Gitlab docker image download fail detected. Retry build: {0}, sha: {1}".format(build_id, sha))
  107. request = self.request_build_retry(project_id, build_id, sha, options)
  108. elif request.text.find("FATAL: invalid argument ") >= 0 and \
  109. request.text.find("[0KERROR: Job failed: exit code ") >= 0:
  110. Logger.addLog("Gitlab build issue detected. Retry build: {0}, sha: {1}".format(build_id, sha))
  111. request = self.request_build_retry(project_id, build_id, sha, options)
  112. elif request.text.find("[0KERROR: Job failed: execution took longer than 3h0m0s seconds") >= 0:
  113. Logger.addLog("Gitlab build issue detected. Retry build: {0}, sha: {1}".format(build_id, sha))
  114. request = self.request_build_retry(project_id, build_id, sha, options)
  115. elif request.text.find("ERROR: Job failed: Error response from daemon: error parsing "
  116. "HTTP 404 response body: invalid character 'p' after top-level value:") >= 0:
  117. Logger.addLog("Gitlab docker image download fail detected. Retry build: {0}, sha: {1}".format(build_id, sha))
  118. request = self.request_build_retry(project_id, build_id, sha, options)
  119. else:
  120. Logger.addError("ApiHandler: wrong command: {0}".format(data[0]))
  121. def request_build_log(self, project, build, options):
  122. return self.send_request(
  123. "{url}/v4/projects/{project}/jobs/{build}/trace".format(
  124. url = options.gitlab_api_url,
  125. project = project,
  126. build = build
  127. ),
  128. options.gitlab_api_token,
  129. False,
  130. options.gitlab_api_retry_count)
  131. def check_retry_build_count(self, sha, options):
  132. if sha not in self.retry_builds:
  133. self.retry_builds[sha] = 0
  134. return self.retry_builds[sha] < options.build_retry_count
  135. def request_build_retry(self, project, build, sha, options):
  136. if self.check_retry_build_count(sha, options) == False:
  137. Logger.addLog("Retry limit reached for: {0}".format(sha))
  138. return
  139. self.retry_builds[sha] = self.retry_builds[sha] + 1
  140. response = self.send_request(
  141. "{url}/v4/projects/{project}/jobs/{build}/retry".format(
  142. url = options.gitlab_api_url,
  143. project = project,
  144. build = build
  145. ),
  146. options.gitlab_api_token,
  147. True,
  148. options.gitlab_api_retry_count)
  149. return response
  150. def retry_build(self, project_id, build_id, sha, options):
  151. if self.check_retry_build_count(sha, options) == False:
  152. Logger.addLog("Retry limit reached for: {0}".format(sha))
  153. return
  154. self.add(("getlog", options, project_id, build_id, sha))