Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions code/__DEFINES/opensearch.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// States of query

/// Query is ready to be built
#define OPENSEARCH_QUERY_STATUS_READY 1
/// Query was unable to be built
#define OPENSEARCH_QUERY_STATUS_BUILD_ERROR 2
/// Query is executing
#define OPENSEARCH_QUERY_STATUS_EXECUTING 3
/// Query failed to execute
#define OPENSEARCH_QUERY_STATUS_FAILED 4
/// Query executed successfully
#define OPENSEARCH_QUERY_STATUS_SUCCESS 5
1 change: 1 addition & 0 deletions code/__DEFINES/subsystems.dm
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@
#define SS_PRIORITY_FZ_TRANSITIONS 88
#define SS_PRIORITY_ROUND_RECORDING 83
#define SS_PRIORITY_SHUTTLE 80
#define SS_PRIORITY_OPENSEARCH 75
#define SS_PRIORITY_EVENT 65
#define SS_PRIORITY_DISEASE 60
#define SS_PRIORITY_DEFENSES 55
Expand Down
116 changes: 116 additions & 0 deletions code/controllers/subsystem/opensearch.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/// Handles querying OpenSearch for logs information
SUBSYSTEM_DEF(opensearch)
name = "OpenSearch"
priority = SS_PRIORITY_OPENSEARCH
runlevels = RUNLEVELS_DEFAULT|RUNLEVEL_LOBBY
wait = 0.2 SECONDS

/// Incrementing id counter to assign to queries
var/query_counter = 1
/// Incrementing counter of completed requests
var/total_requests = 0
/// AList of ID -> /datum/opensearch_query
var/alist/queries = alist()
/// List of /datum/opensearch_query currently performing a request, mapped to their http_request object
var/list/datum/opensearch_query/active = list()
/// Snapshot of the above for scheduling
var/list/datum/opensearch_query/currentrun

/datum/controller/subsystem/opensearch/Initialize()
if(!CONFIG_GET(string/opensearch_host))
can_fire = FALSE
return SS_INIT_NO_NEED
return SS_INIT_SUCCESS

/datum/controller/subsystem/opensearch/stat_entry(msg)
if(!can_fire)
msg = "Disabled"
else
msg = "Completed: [total_requests], Queries: [length(queries)], Active: [length(active)]"
return ..()

/datum/controller/subsystem/opensearch/fire(resumed)
if(!resumed)
currentrun = active.Copy()
while(length(currentrun))
var/datum/opensearch_query/query = currentrun[currentrun.len]
var/datum/http_request/http_request = currentrun[query]
currentrun.len--
handle_request(query, http_request)
if(MC_TICK_CHECK)
return

/// Create a new query and register it
/// [bootstrap] : Important text to seed the query with
/datum/controller/subsystem/opensearch/proc/new_query(bootstrap, boostfield, boostfactor)
RETURN_TYPE(/datum/opensearch_query)
var/id = query_counter++
var/datum/opensearch_query/query = new(id, bootstrap, boostfield, boostfactor)
queries[id] = query
return query

/// Remove a query from controller tracking
/datum/controller/subsystem/opensearch/proc/forget(datum/opensearch_query/query)
if(length(currentrun))
currentrun -= query
active -= query
queries -= query.id

/// Handle a request completion during fire
/datum/controller/subsystem/opensearch/proc/handle_request(datum/opensearch_query/query, datum/http_request/http_request)
PRIVATE_PROC(TRUE)
var/completed = http_request.is_complete()
if(completed)
total_requests++
active -= query
var/datum/http_response/response = http_request.into_response()
if(!response || response.errored)
query.on_error(response)
else if(response.status_code == 200)
query.on_success(response)
else
// This should have different handling to catch OpenSearch errors, but rustg is dumb
// and won't actually let us grab the response body on an opensearch error.
// It'll just trip over itself in parsing and report "host returned status 400"
query.on_error(response)

/// Queues a new HTTP request to the OpenSearch backend:
/// * [query] : The query this request is attributed to. Has no real effect, is just to know who to fire callbacks to.
/// * [endpoint] : The HTTP endpoint to make the request to. For example, /_search
/// * [payload] : The full HTTP payload with a properly built query for the backend
/datum/controller/subsystem/opensearch/proc/queue(datum/opensearch_query/query, endpoint, list/payload)
if(!can_fire || query.query_status != OPENSEARCH_QUERY_STATUS_READY || (query in active))
return FALSE
var/bare_payload = payload
if(islist(payload))
bare_payload = json_encode(payload)
var/datum/http_request/request = new
request.prepare(RUSTG_HTTP_METHOD_GET, "[CONFIG_GET(string/opensearch_host)]/[CONFIG_GET(string/opensearch_pattern)]/[endpoint]", bare_payload, list("Content-Type" = "application/json", "Authorization" = "Basic [CONFIG_GET(string/opensearch_authtoken)]"))
request.begin_async()
active[query] = request
query.query_status = OPENSEARCH_QUERY_STATUS_EXECUTING
. = TRUE


/// The host to make OpenSearch queries to, for example "https://opensearch.cm-ss13.com"
/datum/config_entry/string/opensearch_host
protection = CONFIG_ENTRY_LOCKED

/// The Basic HTTP auth token to use for authentication to OpenSearch, in the form of user:password base64ed
/datum/config_entry/string/opensearch_authtoken
protection = CONFIG_ENTRY_HIDDEN | CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_SENSITIVE

/// Index patterns to be accessed by OpenSearch query tool
/datum/config_entry/string/opensearch_pattern
protection = CONFIG_ENTRY_LOCKED

/// Max results to fetch in an OpenSearch query. Without pagination, OpenSearch limits it to 10000 results on top of that.
/datum/config_entry/number/opensearch_max_results
config_entry_value = 500

/// URL to the OpenSearch-Dashboards instance for the "link to dashboards" button
/datum/config_entry/string/opensearch_dashboards_url
protection = CONFIG_ENTRY_LOCKED

/// OpenSearch-Dashboards Discovery Saved Object that will be used as basis for the link-to-dashboards feature
/datum/config_entry/string/opensearch_dashboards_saved_discover
Loading