Skip to content

Repository files navigation

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.

Design principles

  • Terraform-parity authentication. HTTP Basic with an access key pair: SUMOLOGIC_ACCESSID (username) and SUMOLOGIC_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. The region variable (au, ca, ch, de, eu, fed, in, jp, kr, us1, us2) carries x-stackQL-envVar: SUMOLOGIC_ENVIRONMENT - Terraform's variable - so StackQL resolves it from the environment, and a WHERE region = '...' value always wins. It keeps the previous release's default: us2, which applies at request time only: any-sdk lists a server variable as required whenever its environment variable is unset, so without SUMOLOGIC_ENVIRONMENT a SELECT still falls back to us2 but INSERT / UPDATE / DELETE / EXEC statements must supply region (the docs annotate it "required unless SUMOLOGIC_ENVIRONMENT is set").
  • snake_case user surface. Columns and WHERE / INSERT / UPDATE keys are snake_case over the camelCase wire (snake_case_aliases: true on the provider config plus request.nativeCasing: camel on every method). Nested JSON columns keep wire casing inside the value.
  • Pagination and pushdown. The Sumo Logic limit / token / next convention 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 a WHERE-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 EXEC methods on the resource they act on, not separate non-selectable resources. Asynchronous jobs are <x>_jobs (EXEC start + SELECT status) and <x>_results resources.
  • A durable mapping contract. provider-dev/config/all_services.csv records 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.

Breaking changes from the previous provider release

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 spec
  • ingest_budgets.collectors.getAssignedCollectors (GET /v1/ingestBudgets/{id}/collectors) - operation no longer in the vendor spec
  • ingest_budgets.collectors.removeCollectorFromBudget (DELETE /v1/ingestBudgets/{id}/collectors/{collectorId}) - operation no longer in the vendor spec
  • ingest_budgets.ingest_budgets_v1.createIngestBudget (POST /v1/ingestBudgets) - operation no longer in the vendor spec
  • ingest_budgets.ingest_budgets_v1.deleteIngestBudget (DELETE /v1/ingestBudgets/{id}) - operation no longer in the vendor spec
  • ingest_budgets.ingest_budgets_v1.getIngestBudget (GET /v1/ingestBudgets/{id}) - operation no longer in the vendor spec
  • ingest_budgets.ingest_budgets_v1.listIngestBudgets (GET /v1/ingestBudgets) - operation no longer in the vendor spec
  • ingest_budgets.ingest_budgets_v1.updateIngestBudget (PUT /v1/ingestBudgets/{id}) - operation no longer in the vendor spec
  • ingest_budgets.usage_reset.resetUsage (POST /v1/ingestBudgets/{id}/usage/reset) - operation no longer in the vendor spec
  • lookup_tables.upload.uploadFile (POST /v1/lookupTables/{id}/upload) - skipped in this build (see all_services.csv)

Prerequisites

  • Node.js >= 20 for the provider pipeline, Node.js >= 22.12 for the docs site (Docusaurus 3.10 pulls in mermaid 12), GNU make, bash (Linux, macOS or WSL)
  • A stackql binary for testing ($STACKQL, ./stackql, or on PATH) - 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 install

Makefile

Every 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=us2

1. Download and pin the spec

make 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.

2. Split into service specs

make split

bin/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.

3. Normalize

make pre-normalize normalize

pre_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.

4. Generate mappings

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 write

generate-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.

5. Generate the provider

make generate

which 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: camel on every method except the body-less EXECs (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 SELECT methods 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 for POST /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"}';

6. Test

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 .env

Offline 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 only

UAT

set -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}" shell
SELECT 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;

CI

.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.

7. Publish the provider

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}" shell
registry pull sumologic;

then make smoke-test-live.

8. Generate the web docs

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-serve

The 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.

Deployment (GitHub Pages)

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.

License

MIT License - see LICENSE.

Contributing

Contributions are welcome. Please open an issue or pull request.

About

generate stackql provider for Sumologic from openapi specs

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages