The little things give you away... A collection of various small helper stuff
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

180 lines
5.3 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. 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. if ! valid_column "${column^^}"
  77. then
  78. echo "Invalid sort column: ${column}" >&2
  79. exit 1
  80. fi
  81. done
  82. else
  83. # Default sort order
  84. sortcolumns+=("JOBID")
  85. fi
  86. if [[ "${notable}" ]]
  87. then
  88. column=("cat")
  89. else
  90. column=("column" "-t" $'-s\t')
  91. fi
  92. jobdata="$(curl -s -H "Accept: application/json" "http://dashboard.at.ninjawedding.org/logs/recent?count=1" 2>/dev/null)"
  93. pipelinedata="$(curl -s -H "Accept: application/json" "http://dashboard.at.ninjawedding.org/pipelines" 2>/dev/null)"
  94. if [[ -z "${jobdata}" || -z "${pipelinedata}" ]]
  95. then
  96. echo "Error retrieving job or pipeline data" >&2
  97. exit 1
  98. fi
  99. { echo "${jobdata}"; echo "${pipelinedata}"; echo "${filter}"; } | python3 -c \
  100. '
  101. if True: # For sensible indentation
  102. import json
  103. import sys
  104. import time
  105. def time_ago(diff):
  106. if diff <= 0:
  107. return "now"
  108. elif diff < 60:
  109. return "<1 min ago"
  110. elif diff < 86400:
  111. return (f"{diff // 3600:.0f}h " if diff >= 3600 else "") + f"{(diff % 3600) // 60:.0f}mn ago"
  112. else:
  113. return f"{diff // 86400:.0f}d {(diff % 86400) // 3600:.0f}h ago"
  114. def coloured_time_ago(diff):
  115. if diff >= 300:
  116. return "\x1b[0;31m" + time_ago(diff) + "\x1b[0m"
  117. else:
  118. return time_ago(diff)
  119. jobdata = json.loads(sys.stdin.readline())
  120. pipelinedata = json.loads(sys.stdin.readline())
  121. filter = sys.stdin.readline().strip()
  122. pipelines = {p["id"]: p["nickname"] for p in pipelinedata["pipelines"]}
  123. columns = ("JOBID", "URL", "USER", "PIPENICK", "QUEUED", "STARTED", "LAST ACTIVE") # Duplicated in Bash code!
  124. jobs = []
  125. currentTime = time.time()
  126. for j in jobdata:
  127. jobs.append([
  128. j["job_data"]["ident"],
  129. j["job_data"]["url"],
  130. j["job_data"]["started_by"],
  131. pipelines[j["job_data"]["pipeline_id"]] if j["job_data"]["pipeline_id"] in pipelines else "unknown",
  132. currentTime - j["job_data"]["queued_at"],
  133. currentTime - j["job_data"]["started_at"],
  134. currentTime - j["ts"],
  135. ])
  136. # Filter
  137. if filter:
  138. import re
  139. match = re.match(r"^(?P<column>[A-Za-z ]+)(?P<op>[=<>^$])(?P<value>.*)$", filter)
  140. filterDict = match.groupdict()
  141. filterDict["column"] = filterDict["column"].upper()
  142. assert filterDict["column"] in columns
  143. columnIdx = columns.index(filterDict["column"])
  144. compFunc = {
  145. "=": lambda a, b: a == b,
  146. "<": lambda a, b: a < b,
  147. ">": lambda a, b: a > b,
  148. "^": lambda a, b: a.startswith(b),
  149. "$": lambda a, b: a.endswith(b),
  150. }[filterDict["op"]]
  151. jobs = [job for job in jobs if compFunc(job[columnIdx], filterDict["value"])]
  152. # Sort
  153. sortColumns = ('"$(printf "'%s', " "${sortcolumns[@]}")"')
  154. assert all(column.upper() in columns for column in sortColumns)
  155. sortColumnIdxs = tuple(columns.index(column.upper()) for column in sortColumns)
  156. jobs = sorted(jobs, key = lambda job: tuple(job[columnIdx] for columnIdx in sortColumnIdxs))
  157. # Print
  158. print("\t".join(columns))
  159. for job in jobs:
  160. job[4] = time_ago(job[4])
  161. job[5] = time_ago(job[5])
  162. job[6] = (coloured_time_ago if not "'${nocolours}'" else time_ago)(job[6])
  163. print("\t".join(job))
  164. ' | "${column[@]}"