The little things give you away... A collection of various small helper stuff
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 

193 lignes
5.8 KiB

  1. #!/bin/bash
  2. columns=("JOBID" "URL" "USER" "PIPENICK" "QUEUED" "STARTED" "LAST ACTIVE") # Duplicated in Python code!
  3. function valid_column {
  4. local candidate="$1"
  5. local column
  6. for column in "${columns[@]}"
  7. do
  8. [[ "${candidate}" == "${column}" ]] && return 0
  9. done
  10. return 1
  11. }
  12. sortcolumns=()
  13. filter=
  14. nocolours=
  15. notable=
  16. while [[ $# -gt 0 ]]
  17. do
  18. if [[ "$1" == "--help" || "$1" == "-h" ]]
  19. then
  20. echo "Usage: archivebot-jobs [options]" >&2
  21. echo "Prints a table of current AB jobs" >&2
  22. echo "Options:" >&2
  23. echo " --help, -h Show this message and exit." >&2
  24. echo " --sort [-]COLUMN, -s Sort the table by a column (descending if preceded by '-'). This can be used multiple times to refine the sorting." >&2
  25. echo " --filter FILTER, -f Filter the table for rows where a COLUMN has a certain VALUE. If specified multiple times, only the last value is used." >&2
  26. echo " The FILTER has this format: COLUMN{=|<|>|^|$}VALUE" >&2
  27. echo " = means the value must be exactly as specified; < and > mean it must be less/greater than the specified; ^ and $ mean it must start/end with the specified." >&2
  28. echo " --no-colours, --no-colors Don't colourise the last activity column if it's been a while." >&2
  29. echo " --no-table Raw output without feeding through column(1); columns are separated by tabs." >&2
  30. echo "The COLUMNs are the names of each column, printed in capital letters in the first line of the output." >&2
  31. exit 0
  32. elif [[ "$1" == "--sort" || "$1" == "-s" ]]
  33. then
  34. sortcolumns+=("$2")
  35. shift
  36. elif [[ "$1" == "--filter" || "$1" == "-f" ]]
  37. then
  38. filter="$2"
  39. shift
  40. elif [[ "$1" == "--no-colours" || "$1" == "--no-colors" ]]
  41. then
  42. nocolours=1
  43. elif [[ "$1" == "--no-table" ]]
  44. then
  45. notable=1
  46. else
  47. echo "Unknown option: $1" >&2
  48. exit 1
  49. fi
  50. shift
  51. done
  52. # Validate sortcolumns and filter
  53. if [[ "${filter}" ]]
  54. then
  55. if [[ "${filter}" == *$'\n'* ]]
  56. then
  57. echo "Invalid filter: newlines not allowed" >&2
  58. exit 1
  59. fi
  60. if [[ ! ( "${filter}" == *'='* || "${filter}" == *'<'* || "${filter}" == *'>'* || "${filter}" == *'^'* || "${filter}" == *'$'* ) ]]
  61. then
  62. echo "Invalid filter: ${filter}" >&2
  63. exit 1
  64. fi
  65. column="${filter%%[=<>^$]*}"
  66. if ! valid_column "${column^^}"
  67. then
  68. echo "Invalid filter column: ${column}" >&2
  69. exit 1
  70. fi
  71. fi
  72. if [[ ${#sortcolumns[@]} -gt 0 ]]
  73. then
  74. for column in "${sortcolumns[@]}"
  75. do
  76. column="${column#-}"
  77. if ! valid_column "${column^^}"
  78. then
  79. echo "Invalid sort column: ${column}" >&2
  80. exit 1
  81. fi
  82. done
  83. else
  84. # Default sort order
  85. sortcolumns+=("JOBID")
  86. fi
  87. if [[ "${notable}" ]]
  88. then
  89. column=("cat")
  90. else
  91. column=("column" "-t" $'-s\t')
  92. fi
  93. jobdata="$(curl -s -H "Accept: application/json" "http://dashboard.at.ninjawedding.org/logs/recent?count=1" 2>/dev/null)"
  94. pipelinedata="$(curl -s -H "Accept: application/json" "http://dashboard.at.ninjawedding.org/pipelines" 2>/dev/null)"
  95. if [[ -z "${jobdata}" || -z "${pipelinedata}" ]]
  96. then
  97. echo "Error retrieving job or pipeline data" >&2
  98. exit 1
  99. fi
  100. { echo "${jobdata}"; echo "${pipelinedata}"; echo "${filter}"; } | python3 -c \
  101. '
  102. if True: # For sensible indentation
  103. import json
  104. import sys
  105. import time
  106. def time_ago(diff):
  107. if diff <= 0:
  108. return "now"
  109. elif diff < 60:
  110. return "<1 min ago"
  111. elif diff < 86400:
  112. return (f"{diff // 3600:.0f}h " if diff >= 3600 else "") + f"{(diff % 3600) // 60:.0f}mn ago"
  113. else:
  114. return f"{diff // 86400:.0f}d {(diff % 86400) // 3600:.0f}h ago"
  115. def coloured_time_ago(diff):
  116. if diff >= 300:
  117. return f"\x1b[{0 if diff < 6 * 3600 else 7};31m" + time_ago(diff) + "\x1b[0m"
  118. else:
  119. return time_ago(diff)
  120. jobdata = json.loads(sys.stdin.readline())
  121. pipelinedata = json.loads(sys.stdin.readline())
  122. filter = sys.stdin.readline().strip()
  123. pipelines = {p["id"]: p["nickname"] for p in pipelinedata["pipelines"]}
  124. columns = ("JOBID", "URL", "USER", "PIPENICK", "QUEUED", "STARTED", "LAST ACTIVE") # Duplicated in Bash code!
  125. jobs = []
  126. currentTime = time.time()
  127. for j in jobdata:
  128. jobs.append([
  129. j["job_data"]["ident"],
  130. j["job_data"]["url"],
  131. j["job_data"]["started_by"],
  132. pipelines[j["job_data"]["pipeline_id"]] if j["job_data"]["pipeline_id"] in pipelines else "unknown",
  133. currentTime - j["job_data"]["queued_at"],
  134. currentTime - j["job_data"]["started_at"],
  135. currentTime - j["ts"],
  136. ])
  137. # Filter
  138. if filter:
  139. import re
  140. match = re.match(r"^(?P<column>[A-Za-z ]+)(?P<op>[=<>^$])(?P<value>.*)$", filter)
  141. filterDict = match.groupdict()
  142. filterDict["column"] = filterDict["column"].upper()
  143. assert filterDict["column"] in columns
  144. columnIdx = columns.index(filterDict["column"])
  145. compFunc = {
  146. "=": lambda a, b: a == b,
  147. "<": lambda a, b: a < b,
  148. ">": lambda a, b: a > b,
  149. "^": lambda a, b: a.startswith(b),
  150. "$": lambda a, b: a.endswith(b),
  151. }[filterDict["op"]]
  152. if isinstance(jobs[0][columnIdx], (int, float)):
  153. filterDict["value"] = float(filterDict["value"])
  154. jobs = [job for job in jobs if compFunc(job[columnIdx], filterDict["value"])]
  155. # Sort
  156. class reversor: # https://stackoverflow.com/a/56842689
  157. def __init__(self, obj):
  158. self.obj = obj
  159. def __eq__(self, other):
  160. return other.obj == self.obj
  161. def __lt__(self, other):
  162. return other.obj < self.obj
  163. sortColumns = ('"$(printf "'%s', " "${sortcolumns[@]}")"')
  164. assert all(column.lstrip("-").upper() in columns for column in sortColumns)
  165. sortColumnIdxs = tuple(columns.index(column.lstrip("-").upper()) for column in sortColumns)
  166. jobs = sorted(jobs, key = lambda job: tuple(job[columnIdx] if not column.startswith("-") else reversor(job[columnIdx]) for column, columnIdx in zip(sortColumns, sortColumnIdxs)))
  167. # Print
  168. print("\t".join(columns))
  169. for job in jobs:
  170. job[4] = time_ago(job[4])
  171. job[5] = time_ago(job[5])
  172. job[6] = (coloured_time_ago if not "'${nocolours}'" else time_ago)(job[6])
  173. print("\t".join(job))
  174. ' | "${column[@]}"