master
sh 264 lines 9.22 KB
Raw
1 #!/usr/bin/env bash
2 # get-events.sh -- fetch events of interest from agent-events.
3 #
4 # Index-friendly defaults: 24h time, multi-value selections,
5 # auto version filter (latest stable + latest 3 nightlies).
6 #
7 # Output: JSON dump under
8 # <repo>/.local/audits/query-agent-events/<timestamp>.json
9
10 set -euo pipefail
11
12 usage() {
13 cat <<'EOF'
14 get-events.sh [options]
15
16 Transport:
17 --via cloud|agent (default: cloud)
18
19 Time window:
20 --since '<n>h ago'|'<n>d ago'|<seconds> (default: 24h ago)
21 --before now|<seconds> (default: now)
22
23 Filters (all are AND'd; values within a flag are OR'd):
24 --health <classes> comma-separated; common: all, crash, healthy
25 (default: all)
26 "crash" -> crash-first,crash-loop,crash-repeated,crash-entered
27 "healthy" -> healthy-first,healthy-loop,healthy-repeated,healthy-recovered
28 --exit-cause <causes> comma-separated; common: all, fatal, signal, graceful
29 (default: all)
30 "fatal" -> deliberate-exit class (OOM, disk full, etc.)
31 "signal" -> killed-hard variants
32 "graceful"-> exit instructed/updated/shutdown/no last status
33 --signal <values> comma-separated AE_FATAL_SIGNAL_CODE values
34 example: SIGSEGV/SEGV_MAPERR,SIGBUS/BUS_OBJERR
35 --function <names> comma-separated AE_FATAL_FUNCTION values
36 --version <spec> "auto" (default), "all", or a regex
37 auto -> latest stable + latest 3 nightlies (computed)
38 all -> no version filter
39 else -> regex applied client-side after fetch
40 (multi-value selections require explicit values;
41 use --versions for that)
42 --versions <list> explicit comma-separated AE_AGENT_VERSION values
43 (overrides --version's auto/all/regex)
44 --arch <values> comma-separated AE_HOST_ARCHITECTURE values
45 --os-family <values> comma-separated AE_OS_FAMILY values
46 --query <fts> residual FTS narrower (after structured filters)
47 --facets <names> comma-separated; included in response for grouping
48
49 Response control:
50 --last N page size, default 500
51 --histogram FIELD add a histogram bucket on FIELD
52
53 Output:
54 --output PATH path to write the JSON dump (default: auto under .local/audits/...)
55
56 Other:
57 -h, --help this message
58 -v, --verbose show the constructed payload before fetching
59 EOF
60 }
61
62 # ---------------------------------------------------------------
63 # Argument parsing.
64
65 VIA=cloud
66 SINCE='24h ago'
67 BEFORE=now
68 HEALTH=all
69 EXIT_CAUSE=all
70 SIGNAL=
71 FUNCTION=
72 VERSION=auto
73 VERSIONS_EXPLICIT=
74 ARCH=
75 OS_FAMILY=
76 QUERY=
77 FACETS=
78 LAST=500
79 HISTOGRAM=
80 OUTPUT=
81 VERBOSE=0
82
83 while [ $# -gt 0 ]; do
84 case "$1" in
85 --via) VIA="$2"; shift 2 ;;
86 --since) SINCE="$2"; shift 2 ;;
87 --before) BEFORE="$2"; shift 2 ;;
88 --health) HEALTH="$2"; shift 2 ;;
89 --exit-cause) EXIT_CAUSE="$2"; shift 2 ;;
90 --signal) SIGNAL="$2"; shift 2 ;;
91 --function) FUNCTION="$2"; shift 2 ;;
92 --version) VERSION="$2"; shift 2 ;;
93 --versions) VERSIONS_EXPLICIT="$2"; shift 2 ;;
94 --arch) ARCH="$2"; shift 2 ;;
95 --os-family) OS_FAMILY="$2"; shift 2 ;;
96 --query) QUERY="$2"; shift 2 ;;
97 --facets) FACETS="$2"; shift 2 ;;
98 --last) LAST="$2"; shift 2 ;;
99 --histogram) HISTOGRAM="$2"; shift 2 ;;
100 --output) OUTPUT="$2"; shift 2 ;;
101 -v|--verbose) VERBOSE=1; shift ;;
102 -h|--help) usage; exit 0 ;;
103 *) echo "Unknown option: $1" >&2; usage >&2; exit 2 ;;
104 esac
105 done
106
107 # ---------------------------------------------------------------
108 # Lib + env.
109
110 # shellcheck source=SCRIPTDIR/_lib.sh disable=SC1091
111 source "$(cd "$(dirname "$0")" && pwd)/_lib.sh"
112 agentevents_load_env
113
114 # ---------------------------------------------------------------
115 # Time spec -> relative seconds.
116
117 parse_time() {
118 local s="$1"
119 case "$s" in
120 now) echo 0 ;;
121 *' ago') # "24h ago", "7d ago", "30m ago"
122 local body="${s% ago}"
123 case "$body" in
124 *h) printf -- '-%d' "$(( ${body%h} * 3600 ))" ;;
125 *d) printf -- '-%d' "$(( ${body%d} * 86400 ))" ;;
126 *m) printf -- '-%d' "$(( ${body%m} * 60 ))" ;;
127 *) printf -- '-%d' "${body}" ;;
128 esac
129 ;;
130 -*|0*|[1-9]*) echo "$s" ;;
131 *) echo "Invalid time spec: $s" >&2; exit 2 ;;
132 esac
133 }
134
135 AFTER=$(parse_time "$SINCE")
136 BEFORE_PARSED=$(parse_time "$BEFORE")
137
138 # ---------------------------------------------------------------
139 # Build selections.
140
141 declare -a SELECTION_KEYS=()
142 declare -A SELECTION_VALUES=()
143
144 set_selection() {
145 local key="$1"
146 local csv="$2"
147 [ -z "$csv" ] && return
148 SELECTION_KEYS+=("$key")
149 SELECTION_VALUES[$key]="$csv"
150 }
151
152 case "$HEALTH" in
153 all|"") ;;
154 crash) set_selection AE_AGENT_HEALTH "crash-first,crash-loop,crash-repeated,crash-entered" ;;
155 healthy) set_selection AE_AGENT_HEALTH "healthy-first,healthy-loop,healthy-repeated,healthy-recovered" ;;
156 *) set_selection AE_AGENT_HEALTH "$HEALTH" ;;
157 esac
158
159 case "$EXIT_CAUSE" in
160 all|"") ;;
161 fatal)
162 set_selection AE_EXIT_CAUSE \
163 "no last status,out of memory,disk full,disk almost full,disk read-only,already running,fatal on start,fatal on exit,fatal and exit,exit timeout"
164 ;;
165 signal)
166 set_selection AE_EXIT_CAUSE \
167 "deadly signal,deadly signal on start,deadly signal on exit,deadly signal and exit,killed hard,killed hard on start,killed hard on shutdown,killed hard on exit,killed hard on update,killed hard low ram,killed fatal"
168 ;;
169 graceful)
170 set_selection AE_EXIT_CAUSE \
171 "exit instructed,exit and updated,exit on system shutdown,exit to update,exit no reason,no last status"
172 ;;
173 *)
174 set_selection AE_EXIT_CAUSE "$EXIT_CAUSE"
175 ;;
176 esac
177
178 [ -n "$SIGNAL" ] && set_selection AE_FATAL_SIGNAL_CODE "$SIGNAL"
179 [ -n "$FUNCTION" ] && set_selection AE_FATAL_FUNCTION "$FUNCTION"
180 [ -n "$ARCH" ] && set_selection AE_HOST_ARCHITECTURE "$ARCH"
181 [ -n "$OS_FAMILY" ] && set_selection AE_OS_FAMILY "$OS_FAMILY"
182
183 # Version handling.
184 if [ -n "$VERSIONS_EXPLICIT" ]; then
185 set_selection AE_AGENT_VERSION "$VERSIONS_EXPLICIT"
186 elif [ "$VERSION" = "auto" ]; then
187 echo "[get-events] computing default version filter (latest stable + latest 3 nightlies)..." >&2
188 versions_json="$(agentevents_compute_default_versions "$VIA" "${AFTER#-}")"
189 if [ "$(echo "$versions_json" | jq 'length')" -gt 0 ]; then
190 versions_csv="$(echo "$versions_json" | jq -r 'join(",")')"
191 echo "[get-events] auto versions: $versions_csv" >&2
192 set_selection AE_AGENT_VERSION "$versions_csv"
193 else
194 echo "[get-events] auto version detection found no versions; proceeding without version filter" >&2
195 fi
196 elif [ "$VERSION" = "all" ]; then
197 : # no filter
198 else
199 # Pattern -- best-effort: not multi-value selection, leave it
200 # to the caller to client-side-filter the dump after fetch.
201 echo "[get-events] --version <regex> is not pushed to the server; filter the resulting JSON with jq" >&2
202 fi
203
204 # ---------------------------------------------------------------
205 # Compose payload.
206
207 ns="$(agentevents_namespace)"
208
209 # Build selections object using jq.
210 SELECTIONS_JSON='{}'
211 for key in "${SELECTION_KEYS[@]}"; do
212 csv="${SELECTION_VALUES[$key]}"
213 SELECTIONS_JSON=$(echo "$SELECTIONS_JSON" | jq --arg k "$key" --arg v "$csv" \
214 '.[$k] = ($v | split(","))')
215 done
216
217 # Build the full payload.
218 PAYLOAD=$(jq -nc \
219 --argjson after "$AFTER" \
220 --argjson before "$BEFORE_PARSED" \
221 --argjson last "$LAST" \
222 --arg ns "$ns" \
223 --argjson selections "$SELECTIONS_JSON" \
224 --arg query "$QUERY" \
225 --arg facets_csv "$FACETS" \
226 --arg histogram "$HISTOGRAM" \
227 '
228 {
229 "after": $after,
230 "before": $before,
231 "last": $last,
232 "direction": "backward",
233 "__logs_sources": $ns
234 }
235 | (if ($selections | length) > 0 then .selections = $selections else . end)
236 | (if ($query | length) > 0 then .query = $query else . end)
237 | (if ($facets_csv | length) > 0 then .facets = ($facets_csv | split(",")) else . end)
238 | (if ($histogram | length) > 0 then .histogram = $histogram else . end)
239 ')
240
241 if [ "$VERBOSE" -eq 1 ]; then
242 echo "[get-events] payload:" >&2
243 echo "$PAYLOAD" | jq . >&2
244 fi
245
246 # ---------------------------------------------------------------
247 # Output path.
248
249 if [ -z "$OUTPUT" ]; then
250 audit_dir="$(agentevents_audit_dir)"
251 OUTPUT="$audit_dir/$(date -u +%Y%m%dT%H%M%SZ).json"
252 fi
253
254 # ---------------------------------------------------------------
255 # Fetch.
256
257 echo "[get-events] fetching via $VIA (output: $OUTPUT)..." >&2
258 agentevents_query_function "$VIA" "$PAYLOAD" > "$OUTPUT"
259
260 rows="$(jq '.data | length // 0' "$OUTPUT" 2>/dev/null || echo 0)"
261 echo "[get-events] wrote $rows row(s) to $OUTPUT" >&2
262
263 # Print path on stdout for piping.
264 echo "$OUTPUT"