sumologic provider for stackql
This repository generates and documents the sumologic provider for StackQL, enabling SQL-based query and provisioning operations against the Sumo Logic API - collectors and sources, users, roles and service accounts, access keys, the content library (folders, dashboards, saved searches), monitors, SLOs and muting schedules, partitions, scheduled views, fields and extraction rules, ingest budgets, connections, lookup tables, apps, security policies, tracing and search jobs. The provider is built using the @stackql/provider-utils package from the vendor's published OpenAPI document plus a hand-authored description of the Collector Management API.
- Terraform-parity authentication. HTTP Basic with an access key pair:
SUMOLOGIC_ACCESSID(username) andSUMOLOGIC_ACCESSKEY(password), the variables the Terraform provider reads. - Deployment as a server variable. Every service is generated on the server template
https://api.{region}.sumologic.com/api. Theregionvariable (au,ca,ch,de,eu,fed,in,jp,kr,us1,us2) carriesx-stackQL-envVar: SUMOLOGIC_ENVIRONMENT- Terraform's variable - so StackQL resolves it from the environment, and aWHERE region = '...'value always wins. It keeps the previous release'sdefault: us2, which applies at request time only: any-sdk lists a server variable as required whenever its environment variable is unset, so withoutSUMOLOGIC_ENVIRONMENTaSELECTstill falls back tous2butINSERT/UPDATE/DELETE/EXECstatements must supplyregion(the docs annotate it "required unless SUMOLOGIC_ENVIRONMENT is set"). - snake_case user surface. Columns and
WHERE/INSERT/UPDATEkeys are snake_case over the camelCase wire (snake_case_aliases: trueon the provider config plusrequest.nativeCasing: camelon every method). Nested JSON columns keep wire casing inside the value. - Pagination and pushdown. The Sumo Logic
limit/token/nextconvention is a service-level pagination config, with per-method overrides where the API deviates (nextToken,token, a cursor in the request body). Every documented query or header parameter is aWHERE-clause parameter sent on the wire. - Lifecycle operations on their resources. Actions such as unlock, password reset, pause / start, decommission, copy / move / import / export and role assignment are
EXECmethods on the resource they act on, not separate non-selectable resources. Asynchronous jobs are<x>_jobs(EXEC start+SELECTstatus) and<x>_resultsresources. - A durable mapping contract.
provider-dev/config/all_services.csvrecords every operation's resource, method, SQL verb and object key. Refreshes only add rows for new operations and prune retired ones; a resource name never silently changes between releases. - Deterministic builds. Every pipeline step is a re-runnable script; mapping decisions are rules in
provider-dev/scripts/map_operations.mjs, never hand-edits to CSVs or generated YAML. Scripts validate and fail without writing.
Every method of the previous release (267 methods across 32 services) is dispositioned against this build by operation (path and HTTP verb): 151 carried on the same resource, 105 moved to a renamed or restructured resource, 11 retired; 195 operations are new to this release. The previous provider version stays available in the registry for pinning.
Method names. Operation-derived method names (listUsers, getUser, createUser) become resource-scoped names (list, get, create, update, delete) on every carried resource, and lifecycle operations become EXEC methods on the resource they act on (users.unlock, partitions.decommission, scheduled_views.pause) instead of separate non-selectable resources. Columns and WHERE / INSERT keys are now snake_case (firstName -> first_name); nested JSON values keep the wire casing.
Renamed or restructured resources (previous -> this release):
| Previous resource | This release |
|---|---|
access_keys.personal |
access_keys.personal_access_keys |
account.subdomain_recover |
account.subdomain |
apps.install |
apps.apps |
apps.install_status |
apps.install_jobs |
archive.jobs_count |
archive.job_counts |
connections.incident_templates |
connections.connections |
connections.test |
connections.connections |
content.copy |
content.copy_jobs |
content.copy_status |
content.copy_jobs |
content.delete |
content.delete_jobs |
content.delete_status |
content.delete_jobs |
content.export |
content.export_jobs |
content.export_result |
content.export_results |
content.export_status |
content.export_jobs |
content.folders_admin_recommended |
content.admin_recommended_folder_jobs |
content.folders_admin_recommended_result |
content.admin_recommended_folder_results |
content.folders_admin_recommended_status |
content.admin_recommended_folder_jobs |
content.folders_global |
content.global_folder_jobs |
content.folders_global_result |
content.global_folder_results |
content.folders_global_status |
content.global_folder_jobs |
content.folders_import |
content.import_jobs |
content.folders_import_status |
content.import_jobs |
content.folders_personal |
content.personal_folder |
content.move |
content.items |
content.path |
content.items, content.paths |
content.permissions_add |
content.permissions |
content.permissions_remove |
content.permissions |
dashboards.report_jobs_status |
dashboards.report_jobs |
fields.builtin |
fields.builtin_fields |
fields.disable |
fields.fields |
fields.dropped |
fields.dropped_fields |
fields.enable |
fields.fields |
health_events.resources |
health_events.health_events |
ingest_budgets.ingest_budgets_v2 |
ingest_budgets.ingest_budgets |
ingest_budgets.usage_reset |
ingest_budgets.ingest_budgets |
log_searches.estimated_usage_by_tier |
log_searches.estimated_usage |
lookup_tables.delete_table_row |
lookup_tables.lookup_tables |
lookup_tables.jobs_status |
lookup_tables.jobs |
lookup_tables.row |
lookup_tables.lookup_tables |
lookup_tables.truncate |
lookup_tables.lookup_tables |
monitors.copy |
monitors.monitors |
monitors.disable |
monitors.monitors |
monitors.export |
monitors.monitors |
monitors.import |
monitors.monitors |
monitors.move |
monitors.monitors |
monitors.path |
monitors.monitors, monitors.paths |
monitors.permission_summaries_by_subjects |
monitors.permission_summaries |
monitors.permissions_revoke |
monitors.permissions |
monitors.permissions_set |
monitors.permissions |
partitions.cancel_retention_update |
partitions.partitions |
partitions.decommission |
partitions.partitions |
plan.pending_update_request |
account.pending_update_request |
roles.users |
roles.roles |
saml.lockdown_disable |
saml.lockdown |
saml.lockdown_enable |
saml.lockdown |
scheduled_views.disable |
scheduled_views.scheduled_views |
scheduled_views.pause |
scheduled_views.scheduled_views |
scheduled_views.start |
scheduled_views.scheduled_views |
service_allowlist.addresses_add |
service_allowlist.addresses |
service_allowlist.addresses_remove |
service_allowlist.addresses |
service_allowlist.disable |
service_allowlist.status |
service_allowlist.enable |
service_allowlist.status |
slos.copy |
slos.slos |
slos.export |
slos.slos |
slos.import |
slos.slos |
slos.move |
slos.slos |
slos.path |
slos.paths, slos.slos |
slos.sli |
slos.slos |
tracing.spanquery |
tracing.span_queries |
tracing.spanquery_aggregates |
tracing.span_query_aggregates |
tracing.spanquery_fields |
tracing.span_query_fields |
tracing.spanquery_fields_values |
tracing.span_query_field_values |
tracing.spanquery_pause |
tracing.span_queries |
tracing.spanquery_resume |
tracing.span_queries |
tracing.spanquery_rows_facets |
tracing.span_query_facets |
tracing.spanquery_rows_spans |
tracing.span_query_results |
tracing.spanquery_status |
tracing.span_queries |
tracing.tracequery |
tracing.trace_queries |
tracing.tracequery_fields |
tracing.trace_query_fields |
tracing.tracequery_fields_values |
tracing.trace_query_field_values |
tracing.tracequery_rows_traces |
tracing.trace_query_results |
tracing.tracequery_status |
tracing.trace_queries |
tracing.traces_critical_path |
tracing.critical_paths |
tracing.traces_critical_path_breakdown_service |
tracing.critical_path_service_breakdowns |
tracing.traces_exists |
tracing.trace_existence |
tracing.traces_spans |
tracing.spans |
tracing.traces_spans_billing_info |
tracing.span_billing_info |
tracing.traces_trace_events |
tracing.trace_events |
users.email_request_change |
users.users |
users.mfa_disable |
users.users |
users.password_reset |
users.users |
users.unlock |
users.users |
Retired methods (reason-coded):
dashboards.report_jobs_result.getAsyncReportGenerationResult(GET /v2/dashboards/reportJobs/{jobId}/result) - skipped in this build (see all_services.csv)ingest_budgets.collectors.assignCollectorToBudget(PUT /v1/ingestBudgets/{id}/collectors/{collectorId}) - operation no longer in the vendor specingest_budgets.collectors.getAssignedCollectors(GET /v1/ingestBudgets/{id}/collectors) - operation no longer in the vendor specingest_budgets.collectors.removeCollectorFromBudget(DELETE /v1/ingestBudgets/{id}/collectors/{collectorId}) - operation no longer in the vendor specingest_budgets.ingest_budgets_v1.createIngestBudget(POST /v1/ingestBudgets) - operation no longer in the vendor specingest_budgets.ingest_budgets_v1.deleteIngestBudget(DELETE /v1/ingestBudgets/{id}) - operation no longer in the vendor specingest_budgets.ingest_budgets_v1.getIngestBudget(GET /v1/ingestBudgets/{id}) - operation no longer in the vendor specingest_budgets.ingest_budgets_v1.listIngestBudgets(GET /v1/ingestBudgets) - operation no longer in the vendor specingest_budgets.ingest_budgets_v1.updateIngestBudget(PUT /v1/ingestBudgets/{id}) - operation no longer in the vendor specingest_budgets.usage_reset.resetUsage(POST /v1/ingestBudgets/{id}/usage/reset) - operation no longer in the vendor speclookup_tables.upload.uploadFile(POST /v1/lookupTables/{id}/upload) - skipped in this build (see all_services.csv)
- Node.js >= 20 for the provider pipeline, Node.js >= 22.12 for the docs site (Docusaurus 3.10 pulls in
mermaid12), GNU make, bash (Linux, macOS or WSL) - A
stackqlbinary for testing ($STACKQL,./stackql, or onPATH) - install - yarn for the docs site
- A Sumo Logic access key pair for the live smoke tests - Administration -> Security -> Access Keys in the console
npm installEvery step below is a make target; make help lists them. The two composite targets are:
make all # deps, spec refresh, full pipeline, Breaking Changes, offline + integration + meta-route tests, docs, site build
make smoke-test # live smoke suite against the locally generated provider (sources .env)make all never needs credentials and never bills. Live credentials are read from the environment or a gitignored .env (see .env.example):
SUMOLOGIC_ACCESSID=su...
SUMOLOGIC_ACCESSKEY=...
SUMOLOGIC_ENVIRONMENT=us2make spec # accept the current upstream spec (rewrites the pin)
make spec-check # fail if upstream differs from the pin (CI)Sumo Logic serves its OpenAPI document unversioned from the API reference site of every deployment (https://api.sumologic.com/docs/sumologic-api.yaml); bin/fetch-spec.sh downloads it into provider-dev/downloaded/, validates it with @apidevtools/swagger-parser, and records the date, hash and operation count in provider-dev/config/spec_pin.json. The current pin: OpenAPI 3.0.0, 305 paths, 443 operations, 58 tags.
The Collector Management API (collectors, sources, collector upgrades - the surface the Terraform provider's sumologic_collector and sumologic_*_source resources use) is documented separately and is not in the vendor document. It is maintained by hand in provider-dev/downloaded/sumologic-collector-management-api.yaml (18 operations) and merged in at the split step.
make splitbin/split.mjs splits the pinned spec by vendor tag through the explicit tag -> service map in provider-dev/config/service_names.json (58 tags -> 49 services; tags sharing a name are merged, for example appManagement + appManagementV2 -> apps, contentManagement + contentPermissions + folderManagement -> content, traces + spanAnalytics + serviceMap -> tracing), writes the service titles and descriptions carried into provider.yaml, and adds the collectors service from the hand-authored spec. An operation with a tag missing from the map fails the run, so a refresh that introduces a new tag is a deliberate decision.
make pre-normalize normalizepre_normalize.mjs unwraps the spec's Redoc-relative anchor links ([text](#operation/getFolder)), which would be broken anchors on the docs site. The generic provider-utils pass then flattens allOf, lowers oneOf / anyOf, lifts path-level parameters and wraps the ten bare-array responses (monitor search and playbooks, SAML lists, usage info) in an object envelope so their rows project.
make mappings # committed rows kept, NEW operations derived, retired rows pruned
make mappings-rebuild # re-derive every row (deliberate, reviewed renames only)
make mappings-check # report committed rows that differ from today's rules, no writegenerate-mappings (provider-utils analyze) refreshes the skeleton of provider-dev/config/all_services.csv keeping every committed row; map_operations.mjs derives the mapping for rows that have none. Derivation is generic CRUD heuristics plus an explicit rules table (path regex per service); every rule change is reviewable in the script and its effect in the CSV diff.
| Operation pattern | StackQL verb | Resource / method |
|---|---|---|
GET collection (data[] + next, or apps[], dashboards[], collectors[] ...) |
SELECT |
<resource>.list, objectKey $.data / $.<array> |
GET /{id} ($.collector / $.source where the Collector API wraps the object) |
SELECT |
<resource>.get |
| GET singleton (account status, policies, quotas, personal folder) | SELECT |
<resource>.get |
| POST with a body | INSERT |
<resource>.create |
PUT /{id} or singleton (Sumo Logic updates replace the object) |
UPDATE |
<resource>.update |
| PATCH, with the sibling PUT (SCIM) | UPDATE / REPLACE |
users.update / users.replace |
| DELETE | DELETE |
<resource>.delete |
lifecycle / action (unlock, pause, decommission, copy, move, import, export, rotate, assign_user ...) |
EXEC |
on the parent resource |
| async job start / status / result | EXEC / SELECT |
<x>_jobs.start, <x>_jobs.get, <x>_results.get |
| POST without a body | EXEC |
(an INSERT must carry a request schema) |
| deprecated v1 source templates, multipart lookup upload, PDF report result | skipped | reason-coded in the CSV |
Mapping results for the pinned spec: 461 operations -> 199 SELECT, 50 INSERT, 53 UPDATE, 1 REPLACE, 49 DELETE, 99 EXEC, 10 skipped; 163 resources across 50 services. Where the vendor keeps a v1 and a v2 surface for the same entity both are mapped and v2 carries a _v2 suffix (roles_v2, apps_v2, metrics_searches_v2), matching the Terraform resource names.
make generatewhich runs generate-provider with the region server template (provider-dev/config/servers.json), the basic-auth provider config with snake_case_aliases (provider_config.json), the token / next pagination service config (service_config.json) and --naive-req-body-translate (top-level request body properties are INSERT / UPDATE columns), then post_process.mjs:
request.nativeCasing: camelon every method except the body-lessEXECs (a request block without a body schema breaks stackql's EXEC analyzer), with the body media type on the methods that have one- object keys on
SELECTmethods backed by a POST (the OpenTelemetry collectors list) - pagination overrides:
nextToken(data archiving destinations, logs data forwarding destinations and rules),token(saved log searches), and the cursor in the request body forPOST /v1/otCollectors
The Collector Management API wraps request bodies in collector / source objects, so those INSERTs take one JSON-valued column - the edge case naive translate cannot flatten:
INSERT INTO sumologic.collectors.collectors (collector)
SELECT '{"name": "stackql-hosted", "collectorType": "Hosted", "category": "stackql/demo"}';Four layers; the first three run in make test and need no credentials.
make test-offline # SHOW / DESCRIBE assertions against the local file registry
make test-integration # row-level tests against the mock Sumo Logic API
make test-meta # SHOW / DESCRIBE over every service, resource and method via a local stackql server
make smoke-test # live, needs .envOffline validation (tests/offline_validation.mjs) asserts the 50 services and the resource inventory of the key services, the region variable (never a required parameter; x-stackQL-envVar SUMOLOGIC_ENVIRONMENT, default us2), the snake_case columns, the lifecycle EXECs on users, monitors, scheduled_views and lookup_tables, the SCIM REPLACE, the skipped operations and the SHOW INSERT templates.
Integration tests (tests/integration/) run the provider against an in-process mock of the Sumo Logic API serving the vendor's wire shapes (paginated data / next lists, the Collector API wrappers, bare arrays, the token and nextToken cursors, the POST-driven list with a body cursor, SCIM JSON, a JSON-string scalar, the error envelope) on http://127.0.0.1:<port>/{region}/api, so the region segment of every request is recorded. They assert basic auth, the default / environment / WHERE resolution of region, multi-page traversal, WHERE email pushdown, snake -> camel translation of WHERE, INSERT and UPDATE keys, a user and a role lifecycle with the unlock and assign_user / remove_user EXECs, a hosted collector and HTTP source lifecycle through the collector / source wrappers, partitions.decommission, the monitors search, the SCIM list, the account owner scalar, nested JSON columns and the 404 envelope.
Meta-route tests walk every SHOW SERVICES / SHOW RESOURCES / SHOW EXTENDED METHODS / DESCRIBE EXTENDED route through stackql srv and fail on a resource whose select method has no columns (two scalar responses, the account owner string and the SAML metadata XML, are allowed).
Smoke tests (tests/smoke_test.mjs) run against a real account: read smokes over users (including a limit = 1 traversal that must return every user), roles (with a name pushdown), service accounts, access keys, collectors and sources, the personal folder, dashboards, monitors, partitions, fields and the field quota, scheduled views, ingest budgets, connections, tokens, log searches, the app catalog, health events and the password and audit policies; then a disposable write lifecycle - a hosted collector with an HTTP source, a role (INSERT / UPDATE / DELETE), a folder in the personal folder (INSERT / UPDATE, then the asynchronous delete job started with EXEC and polled with SELECT) and a monitor folder. Nothing created is billable (hosted collectors, sources with no data, roles, folders); everything is named stackql-smoke-<stamp> and swept before and after the run.
make smoke-test # local provider
make smoke-test SMOKE_ARGS="--read-only" # no writes
make smoke-test-live # the published provider (REGISTRY PULL sumologic first)
make smoke-cleanup # sweep breadcrumbs onlyset -a; source .env; set +a
REG_ROOT="$(pwd)/provider-dev/openapi"
REG="{\"url\":\"file://${REG_ROOT}\",\"localDocRoot\":\"${REG_ROOT}\",\"verifyConfig\":{\"nopVerify\":true}}"
stackql --registry="${REG}" shellSELECT id, first_name, last_name, email, is_active FROM sumologic.users.users;
SELECT id, name, collector_type, alive FROM sumologic.collectors.collectors;
SELECT id, name, analytics_tier, retention_period FROM sumologic.partitions.partitions;.github/workflows/build-and-test.yml builds from the pinned spec (warning on upstream drift), fails on uncommitted generation drift, checks the Breaking Changes section, runs the offline, integration and meta-route layers and the docs generation on every push and PR; runs the secret-gated live smoke suite on pushes; and a weekly spec-drift job that opens an issue when the served spec moves. The web workflows build and deploy the microsite from main.
Push the sumologic dir (provider-dev/openapi/src/sumologic) to providers/src in a feature branch of stackql-provider-registry and follow the registry release flow. Pull and verify from the dev registry:
export DEV_REG="{ \"url\": \"https://registry-dev.stackql.app/providers\" }"
stackql --registry="${DEV_REG}" shellregistry pull sumologic;then make smoke-test-live.
make docs # generate-docs --snake-case-aliases + website/scripts/sanitize-docs.mjs
make docs-build # yarn install && yarn build (vendors the shared config; needs GitHub access)
make docs-serveThe microsite (website/) is Docusaurus 3.10 on the shared stackql/docusaurus-config, vendored into .shared-config/ at build time. Site-local files are the provider identity (website/provider.js), a thin docusaurus.config.js (which also turns on showLastUpdateTime, so every page carries a "Last updated on ..." stamp from git history), sidebars.js, the MDX sanitizer, src/ and static/ (with CNAME pinning sumologic-provider.stackql.io). The landing page is mastered in provider-dev/docgen/provider-data/headerContent1.txt and headerContent2.txt (installation, authentication, deployment, casing, pagination and pushdown, and the getting-started queries); edit those and re-run make docs.
The microsite is deployed with GitHub Pages through GitHub Actions - there is no Netlify site and no gh-pages branch. .github/workflows/prod-web-deploy.yml runs on every push to main that touches website/** (which includes the regenerated website/docs), builds the site and publishes website/build with actions/upload-pages-artifact and actions/deploy-pages; test-web-deploy.yml test-builds it on pull requests. website/static/CNAME is copied into every build, so the custom domain is set on each deployment.
One-time repository setup: Settings -> Pages -> Build and deployment -> Source: GitHub Actions; Custom domain: sumologic-provider.stackql.io; Enforce HTTPS on. DNS:
| Source Domain | Record Type | Target |
|---|---|---|
| sumologic-provider.stackql.io | CNAME | stackql.github.io. |
MIT License - see LICENSE.
Contributions are welcome. Please open an issue or pull request.