Skip to content

feat(integrations): migrate incident.io, Tracer, MongoDB Atlas, SigNoz, Jenkins, PagerDuty, Dagster, Temporal and Helm onto the shared setup flow#4196

Merged
muddlebee merged 4 commits into
mainfrom
feat/setup-specs-batch3
Jul 22, 2026
Merged

feat(integrations): migrate incident.io, Tracer, MongoDB Atlas, SigNoz, Jenkins, PagerDuty, Dagster, Temporal and Helm onto the shared setup flow#4196
muddlebee merged 4 commits into
mainfrom
feat/setup-specs-batch3

Conversation

@muddlebee

Copy link
Copy Markdown
Collaborator

Part of #4168. Follows #4191 (batch 1) and #4194 (batch 2, still open).

Describe the changes you have made in this PR -

Third migration batch onto the shared setup flow: incident.io, Tracer, MongoDB Atlas, SigNoz, Jenkins, PagerDuty, Dagster, Temporal, Helm. All nine were "N fields + a registered verifier" with no branching prompts and — unlike batch 2 — no env-var name mismatches, so this batch needed no new spec features.

Two more wizard configurators were silently dropping credentials

Configurator Bug
_configure_pagerduty called sync_env_values({}) — neither PAGERDUTY_API_KEY nor PAGERDUTY_BASE_URL reached any tier but the store
_configure_betterstack* same — sync_env_values({}), all four fields store-only

*Better Stack itself is not migrated in this batch (see below) — its wizard bug is left in place and flagged for whoever picks it up.

That makes seven such bugs across three batches (Datadog, Honeycomb, Coralogix, Sentry, Vercel, and now PagerDuty). _configure_incident_io, _configure_jenkins and _configure_dagster were already correct — migrated anyway so persistence has one implementation instead of two that happen to agree today.

A false-positive secret classification, caught by the round-trip test

config.env_file.is_sensitive_env_key() routes any env var whose name ends in key to the keyring. MONGODB_ATLAS_PUBLIC_KEY matched that even though it's a paired public identifier (used alongside a private key for Digest Auth), not a secret on its own — tests/integrations/test_cli_spec_setup.py's keyring/env split assertion caught this immediately (it expected the public key in .env, got it in the keyring instead). Fixed the same way DISCORD_PUBLIC_KEY already was: added to the _NON_SECRET_ENV_KEYS allowlist in config/env_file.py, with a test case in tests/cli/wizard/test_env_sync.py.

Tracer needed its own round-trip check

integrations._catalog_impl.load_env_integrations() is the function every other spec's env round-trip goes through — but Tracer's env-only discovery isn't in it at all. It's a top-level fallback inside resolve_effective_integrations() instead (JWT_TOKEN checked directly, no per-vendor block in the env loader). tests/integrations/test_setup_spec_env_round_trip.py now special-cases Tracer to assert against resolve_effective_integrations() rather than load_env_integrations() — same guarantee, correct target.

Helm's env-only discovery has a separate opt-in gate

load_env_integrations() only surfaces Helm when OSRE_HELM_INTEGRATION is truthy — a deliberate manual switch (Helm shells out to a local binary) unrelated to any credential field. Not a bug, but the round-trip test needed to set that flag alongside the persisted values to exercise the discovery path at all; documented in integrations/helm/setup.py's docstring so the next person doesn't mistake it for a missed SetupField.

Betterstack and RDS were deliberately left out

  • betterstack's sources field is a list downstream (betterstack_extract_params does list(bs.get("sources", []))), but every other SetupField is a plain string — persisting the field as-is would silently corrupt it (list("a,b") iterates characters). Needs a small fix to integrations/betterstack/__init__.py to normalize either shape, which is a real code change beyond wiring a spec, not a mechanical migration.
  • rds's current _setup_rds CLI handler prompts for host/port/database/username/password, but integrations/rds/__init__.py's actual config (classify, rds_is_available, rds_extract_params) reads db_instance_identifier/region — fields the handler never asks for. opensre integrations setup rds has likely never produced a usable RDS integration. Worth its own PR rather than folding a behavior fix into a batch of otherwise-mechanical migrations.

Both are called out on #4168 for whoever picks them up next.

Demo/Screenshot for feature changes and bug fixes -

PagerDuty verify-before-persist, sandboxed HOME, bad key:

$ opensre integrations setup pagerduty

  Setting up pagerduty

❯ PagerDuty API key **********
❯ PagerDuty API base URL (press Enter to use default) https://api.pagerduty.com

  Validating pagerduty credentials...
  error: Connection failed: 401

$ cat $HOME/.opensre/integrations.json   →  (nothing saved)
$ cat $HOME/.env                          →  (nothing saved)

Before this change, _configure_pagerduty (the wizard path) would have saved the key to the store even after a failed probe elsewhere reported "ok" — the store and the failed check were never the same call.


Code Understanding and AI Usage

Did you use AI assistance (ChatGPT, Claude, Copilot, etc.) to write any part of this code?

  • No, I wrote all the code myself
  • Yes, I used AI assistance (continue below)

If you used AI assistance:

  • I have reviewed every single line of the AI-generated code
  • I can explain the purpose and logic of each function/component I added
  • I have tested edge cases and understand how the code handles them
  • I have modified the AI output to follow this project's coding standards and conventions

Explain your implementation approach:

Same problem as #4191/#4194 — three surfaces configured integrations and disagreed about where credentials land, so whether an integration blocked make deploy depended on which command you used. This batch moves nine more onto the one flow.

I picked these nine specifically because none of them have branching prompts, either/or field groups, or post-save side effects — the three gaps #4168 calls out as needing new spec features before they can move. Grafana, the databases, GitHub, AWS, Discord, Slack and Rocket.Chat are still blocked on those; Better Stack and RDS turned out to have their own vendor-specific issues once I looked closely (see above), so they're deferred rather than folded in here.

Key pieces:

  • integrations/<vendor>/setup.py × 9 — declarative field lists; no logic.
  • config/constants/<vendor>.py × 9 — new env-name modules, imported by both the writer (spec) and the reader (load_env_integrations / resolve_effective_integrations), so the two sides can't drift silently.
  • Wizard configurators for incident.io, Jenkins, PagerDuty and Dagster collapse to one line each via configure_from_spec; Tracer, MongoDB Atlas, SigNoz, Temporal and Helm never had a wizard path (only opensre integrations setup <service>), so there was nothing to migrate there.

Edge cases covered by the extended parametrized suites: blank required field fails on that field rather than after every prompt (skipped only for Helm and Dagster, where every field now has a default — verified next() would raise StopIteration there before excluding them); defaulted fields (PagerDuty's base URL, Temporal's namespace, Helm's binary path) accept a bare enter; optional fields (incident.io's base URL, Dagster's API token, Temporal's API key, Helm's context/kubeconfig/namespace) may be left blank and clear every tier when blanked; failed verification persists nothing anywhere.

One mutation test to confirm the round-trip guard actually catches drift: pointed Temporal's namespace at a wrong env name and confirmed the suite fails with the exact mismatch, then reverted.


Checklist before requesting a review

  • I have added proper PR title and linked to the issue
  • I have performed a self-review of my code
  • I can explain the purpose of every function, class, and logic block I added
  • I understand why my changes work and have tested them thoroughly
  • I have considered potential edge cases and how my code handles them
  • If it is a core feature, I have added thorough tests
  • My code follows the project's style guidelines and conventions

…z, Jenkins, PagerDuty, Dagster, Temporal and Helm onto the shared setup flow

Third migration batch (#4168): nine integrations onto IntegrationSetupSpec.
No name mismatches this time, but two more wizard configurators
(PagerDuty, and the same class of bug already seen elsewhere) were
dropping credentials before persistence — both configurators now
delegate to configure_from_spec like every other migrated integration.

Also fixes a false-positive: MONGODB_ATLAS_PUBLIC_KEY's terminal token
("key") routed it to the keyring even though it is a paired public
identifier, not a secret — added to the same env_file.py allowlist that
already covers DISCORD_PUBLIC_KEY.
@github-actions

Copy link
Copy Markdown
Contributor

Greptile code review

This repo uses Greptile for automated review. Before merge, aim for Confidence Score: 5/5 with zero unresolved review threads — see CONTRIBUTING.md.

Run a review — add a PR comment with:

@greptile review

Give it ~5-10 minutes (sometimes longer) for results, then fix feedback and re-trigger until you reach Confidence Score: 5/5.

Optional: automate with the greploop skill.

@greptile-apps

greptile-apps Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR migrates nine more integrations (incident.io, Tracer, MongoDB Atlas, SigNoz, Jenkins, PagerDuty, Dagster, Temporal, Helm) onto the shared apply_setup / IntegrationSetupSpec flow introduced in earlier batches, fixing a class of bugs where sync_env_values({}) was called with an empty dict so credentials never reached the .env or keyring tiers.

  • PagerDuty bug fixed: _configure_pagerduty previously called sync_env_values({}), so PAGERDUTY_API_KEY and PAGERDUTY_BASE_URL were store-only; the new spec routes them correctly to the keyring and .env respectively.
  • MongoDB Atlas public-key mis-classification fixed: MONGODB_ATLAS_PUBLIC_KEY ends in key and was being routed to the keyring; added to _NON_SECRET_ENV_KEYS (same fix as DISCORD_PUBLIC_KEY) with a test asserting .env placement.
  • Tracer special-casing in round-trip tests: Tracer's env-only discovery lives in resolve_effective_integrations rather than load_env_integrations, so the round-trip test targets that function; a comment documents the exception clearly.

Confidence Score: 5/5

Safe to merge — the nine integrations land on the existing shared setup flow without new logic paths, and the two bugs fixed (PagerDuty credential drop, MongoDB Atlas public-key mis-routing) are both covered by round-trip tests that would have caught any regression.

The change is mechanical migration backed by a layered test suite: per-spec CLI tests (prompt → store), end-to-end round-trip tests (write → catalog read-back), wizard flow tests (retry/skip secret), and a new unit test for blank-value keyring deletion. Each new spec is cross-checked against the catalog's actual env-var reader rather than a mock, so the tests would catch naming drift between the writer and reader sides. No new conditional logic was introduced.

No files require special attention.

Important Files Changed

Filename Overview
config/env_file.py Adds MONGODB_ATLAS_PUBLIC_KEY to _NON_SECRET_ENV_KEYS so the paired public key is routed to .env rather than the keyring — same pattern as the existing DISCORD_PUBLIC_KEY exception.
integrations/pagerduty/setup.py New spec for PagerDuty; fixes the wizard bug where sync_env_values({}) silently dropped PAGERDUTY_API_KEY and PAGERDUTY_BASE_URL from every tier except the store.
integrations/tracer/setup.py New spec for Tracer; correctly maps jwt_token to JWT_TOKEN (keyring) and base_url to TRACER_API_URL (.env), matching the existing catalog_impl.py discovery path.
integrations/helm/setup.py New spec for Helm; docstring correctly explains the OSRE_HELM_INTEGRATION opt-in gate that is not part of any SetupField, preventing future confusion about the missing flag.
surfaces/cli/wizard/configurators/alerting.py Collapses _configure_pagerduty and _configure_incident_io to single-line configure_from_spec calls; removes the sync_env_values({}) bug in _configure_pagerduty.
tests/integrations/test_setup_spec_env_round_trip.py Extends the end-to-end round-trip test suite for all nine new specs; correctly special-cases Tracer via resolve_effective_integrations and sets OSRE_HELM_INTEGRATION after _restore_environment for Helm.
tests/cli/wizard/test_env_sync.py Adds MONGODB_ATLAS_PUBLIC_KEY to the non-secret assertions, and a new test verifying that sync_env_secret with a blank value actually deletes the stale keyring entry.
tests/cli/wizard/test_flow.py Updates monkeypatching targets from _dagster_configurator to _setup_flow module (correct location post-migration), adds _stub_dagster_setup helper, and updates the OSS-path assertion to expect a blank token write-through.
config/constants/jenkins.py Defines JENKINS_BASE_URL_ENV=JENKINS_URL and JENKINS_USERNAME_ENV=JENKINS_USER; values match the historical env var names used by jenkins_config_from_env and the old wizard, verified by the round-trip test.
tests/integrations/test_cli_spec_setup.py Extends CLI spec setup parametric tests for all nine new integrations; correctly excludes helm and dagster from the blank-required-field test since all their fields carry defaults or are optional.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[opensre integrations setup service] --> B[_setup_service in cli.py]
    B --> C[_run_spec_setup with SPEC]
    C --> D[IntegrationSetupSpec with fields and verify fn]
    D --> E[apply_setup]
    E --> F{_collect_credentials applies defaults}
    F -->|required field blank| G[SetupOutcome ok=False nothing persisted]
    F -->|all fields present| H{_verify calls spec.verify}
    H -->|failed| G
    H -->|passed| I[_persist_env iterates fields with env_var]
    I --> J{is_sensitive_env_key check}
    J -->|Yes| K[sync_env_secret to keyring]
    J -->|No| L[sync_env_values to .env]
    K --> M[upsert_integration to store]
    L --> M
    M --> N[SetupOutcome ok=True]
Loading

Reviews (2): Last reviewed commit: "merge(main): resolve conflicts with setu..." | Re-trigger Greptile

Comment on lines +13 to +27
service="dagster",
fields=(
SetupField(
name=ENDPOINT_FIELD,
label="Dagster GraphQL endpoint",
prompt=(
"Dagster GraphQL endpoint "
"(e.g. http://localhost:3000 or https://<org>.dagster.plus/<deployment>)"
),
env_var=DAGSTER_ENDPOINT_ENV,
default="http://localhost:3000",
),
SetupField(
name=API_TOKEN_FIELD,
label="Dagster Cloud API token",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 CLI path now silently defaults endpoint to localhost

The wizard configurator always prefilled http://localhost:3000 when no stored credential existed, so the default here is consistent for the wizard path. However, the old _setup_dagster CLI handler rejected a blank endpoint immediately with _die("endpoint is required."), while _run_spec_setup now applies the spec's default and proceeds to verify http://localhost:3000. A Dagster Cloud user who accidentally presses Enter at the endpoint prompt will now receive a connection-refused error from the verifier instead of the earlier "endpoint is required" message. That's still a failure, but the failure reason is less obvious. Consider either keeping the field required=True with no default (restore the old "required" guard) or making it required=False with the current default so the intent is unambiguous.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Greptile flagged on #4196: apply_setup calls sync_env_secret for every
secret field on every save (submitted or not), relying on a blank value
deleting the old keyring entry rather than being skipped — but nothing
exercised that path directly, only implied it through a mocked wizard
test. Add a real-keyring (MemoryKeyring) test for it.
@muddlebee

Copy link
Copy Markdown
Collaborator Author

Added a direct test: test_sync_env_secret_with_blank_value_deletes_the_stored_secret in tests/cli/wizard/test_env_sync.py, using the same MemoryKeyring backend as the existing test_sync_env_values_routes_secrets_to_keyring. It writes a real value via sync_env_secret, then writes blank, and asserts resolve_env_credential comes back empty — so the deletion path is now asserted directly rather than only implied through the mocked wizard test.

Keep both migration batches' constants, CLI setup cases, and env
round-trip coverage. Drop wizard health validators deleted on either
side (gitlab/sentry/posthog/vercel and dagster/pagerduty/incident_io).
@muddlebee

Copy link
Copy Markdown
Collaborator Author

@greptile review

@muddlebee muddlebee added the automerge Squash-merge automatically once CI checks are green label Jul 22, 2026
Waiting on Greptile Review strands labeled PRs after the last Actions
workflow_run retry, since Greptile is external and never re-triggers
Auto-merge. Treat it like vale-spellcheck; review stays a human gate.
@muddlebee
muddlebee merged commit ce295bf into main Jul 22, 2026
24 checks passed
@muddlebee
muddlebee deleted the feat/setup-specs-batch3 branch July 22, 2026 14:46
@github-actions

Copy link
Copy Markdown
Contributor

🛸 Aliens watching our repo just upgraded @muddlebee's threat level to: do not engage — too competent. 👽


👋 Join us on Discord - OpenSRE : hang out, contribute, or hunt for features and issues. Everyone's welcome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automerge Squash-merge automatically once CI checks are green

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant