From 13cf9a30cb4149f19ab6d60793ec6fb2b7c7cb56 Mon Sep 17 00:00:00 2001 From: John Coffey Date: Fri, 21 Aug 2026 20:53:32 -0700 Subject: [PATCH] Rebrand: Sentry -> Cairn OBS Full rebrand across cosmetic branding, code identifiers, and infrastructure/data-plane naming, using the supplied Cairn OBS logo package. Cosmetic: favicon/logo swap (also closes a stale license-audit finding -- the old favicon was SvelteKit's unreplaced scaffold logo), new centered welcome landing page, larger/legible sidebar logo, page titles, CLAUDE.md/README/docs prose. Code identifiers: Go module path github.com/sentry/sentry -> github.com/cairnobs/cairnobs across all 13 modules and ~91 files (protoc regenerated); Rust crates sentry-agent/sentry-parser/sentry-search -> cairnobs-*; CLI sentryctl -> cairnobsctl; Terraform provider fully renamed (sentry_dashboard etc. -> cairnobs_dashboard, provider type, env vars); every session/auth cookie name; agent config paths and Windows service identity. Deliberately preserved: the gRPC wire protocol's protobuf packages (sentry.logs.v1, sentry.agent.v1) and their Go import directory (proto/sentry/...) -- renaming the wire-level package would break every currently-deployed agent binary (confirmed two real hosts, including mail.inbuxa.com, are actively streaming through this exact contract) until rebuilt and redeployed in lockstep with an ingest cutover. Only the Go module path wrapping the generated code changes. Infrastructure: every docker-compose container name (root and three component-level compose files); the Helm chart (directory, Chart.yaml, named-template helpers, all templates, values.yaml image repos); Kubernetes Operator (CRD group sentry.io -> cairnobs.io, both CRD YAML files, Go identifiers, RBAC markers); the coupled enterprise/tenantcrd package. Caught and fixed real path-coupling bugs along the way: the Helm chart's search/ingest volume mounts and the dev-only-credential detection constant vs. docker-compose.yml's literal values had to move together or a security warning would have silently stopped firing. Data plane: Postgres database sentry_metadata -> cairnobs_metadata and role sentry -> cairnobs; ClickHouse database sentry -> cairnobs; Kafka topic sentry.logs.raw -> cairnobs.logs.raw and its consumer groups. Source-level defaults, docker-compose.yml, and every migrate.sh/ provision script default updated together; already-applied migration files left untouched per this repo's immutable-migration convention. Verified at every layer: all 13 Go modules build/vet/test clean, both Rust workspaces (agent, search) build/clippy/test clean, npm run check/ build clean, docker compose config validates on all four compose files. Live-verified against a real docker stack multiple times through this work, including a final fresh-volume run confirming the actual renamed Postgres database/role, ClickHouse database, and Kafka topic all work end to end with a real login and query, zero console errors. --- .github/workflows/license-compliance.yml | 2 +- .gitignore | 2 +- CLAUDE.md | 34 +-- agent/Cargo.lock | 50 ++-- agent/Cargo.toml | 2 +- agent/Dockerfile | 8 +- agent/README.md | 36 +-- .../Cargo.toml | 8 +- .../{sentry-agent => cairnobs-agent}/build.rs | 0 .../config/agent.example.toml | 18 +- .../src/batch.rs | 0 .../src/config.rs | 14 +- .../src/grpc.rs | 0 .../src/main.rs | 14 +- .../src/metrics.rs | 0 .../src/service.rs | 8 +- .../src/source/etw.rs | 4 +- .../src/source/file_tail.rs | 0 .../src/source/journald.rs | 0 .../src/source/mod.rs | 0 .../src/source/windows_eventlog.rs | 2 +- .../Cargo.toml | 2 +- .../src/lib.rs | 0 agent/deny.toml | 2 +- alerting/README.md | 2 +- alerting/cmd/alerting/main.go | 22 +- alerting/go.mod | 2 +- alerting/internal/config/config.go | 2 +- alerting/internal/delivery/delivery_test.go | 2 +- alerting/internal/delivery/pagerduty.go | 4 +- alerting/internal/delivery/webhook.go | 2 +- alerting/internal/evaluator/condition.go | 4 +- alerting/internal/evaluator/condition_test.go | 4 +- alerting/internal/evaluator/evaluator.go | 8 +- alerting/internal/evaluator/transitions.go | 2 +- .../internal/evaluator/transitions_test.go | 2 +- alerting/internal/httpapi/handler.go | 4 +- alerting/internal/httpapi/handler_test.go | 4 +- alerting/internal/queryclient/client.go | 2 +- alerting/internal/sessioncheck/middleware.go | 2 +- .../sessioncheck_integration_test.go | 2 +- api/README.md | 2 +- api/agents/handler.go | 2 +- api/agents/handler_test.go | 4 +- api/ai/aiapi/handler.go | 12 +- api/ai/aiapi/handler_test.go | 4 +- api/ai/aiapi/integration_test.go | 4 +- api/ai/costguard/costguard.go | 2 +- api/ai/costguard/costguard_test.go | 2 +- api/ai/grounding/grounding.go | 4 +- api/ai/grounding/grounding_test.go | 4 +- api/ai/provider/ollama/ollama.go | 2 +- api/ai/provider/ollama/ollama_test.go | 2 +- api/ai/provider/ollama/prompts.go | 16 +- api/ai/router/router.go | 2 +- api/ai/router/router_test.go | 2 +- api/authz/httpauthz_test.go | 4 +- api/cmd/api/main.go | 30 +-- api/dashboards/handler.go | 4 +- api/dashboards/handler_test.go | 2 +- api/dashboards/permissions.go | 2 +- api/dashboards/store_integration_test.go | 2 +- api/go.mod | 6 +- api/internal/config/config.go | 2 +- api/internal/config/config_test.go | 2 +- api/internal/querylang/parser/parser.go | 4 +- api/internal/querylang/parser/parser_test.go | 2 +- api/internal/querylang/planner/planner.go | 6 +- .../querylang/planner/planner_test.go | 2 +- api/localauth/authorizer.go | 6 +- api/localauth/fake_test.go | 2 +- api/localauth/handler.go | 8 +- api/localauth/handler_test.go | 2 +- api/localauth/ratelimit_test.go | 2 +- api/localauth/store.go | 2 +- api/localauth/store_integration_test.go | 4 +- api/logretention/handler.go | 2 +- api/logretention/handler_test.go | 2 +- api/logretention/store.go | 2 +- api/queryapi/handler.go | 10 +- api/queryapi/handler_test.go | 4 +- api/querylang/executor/executor.go | 2 +- api/querylang/executor/executor_test.go | 2 +- api/querylang/executor/sql.go | 2 +- api/searchclient/client.go | 2 +- cli/Dockerfile | 8 +- cli/README.md | 38 +-- .../{sentryctl => cairnobsctl}/auth_test.go | 6 +- .../{sentryctl => cairnobsctl}/cmd_agents.go | 44 ++-- .../cmd_agents_test.go | 0 .../{sentryctl => cairnobsctl}/cmd_alerts.go | 8 +- .../cmd_alerts_test.go | 2 +- .../cmd_dashboards.go | 24 +- .../cmd_dashboards_test.go | 0 .../{sentryctl => cairnobsctl}/cmd_ping.go | 2 +- .../{sentryctl => cairnobsctl}/cmd_query.go | 4 +- .../cmd_query_test.go | 0 .../{sentryctl => cairnobsctl}/cmd_users.go | 18 +- .../cmd_users_test.go | 4 +- .../{sentryctl => cairnobsctl}/httpclient.go | 2 +- cli/cmd/{sentryctl => cairnobsctl}/main.go | 56 ++--- .../{sentryctl => cairnobsctl}/main_test.go | 4 +- cli/go.mod | 2 +- deploy/README.md | 18 +- deploy/helm/{sentry => cairnobs}/Chart.yaml | 4 +- deploy/helm/{sentry => cairnobs}/README.md | 14 +- .../crds/cairnobs.io_tenants.yaml} | 4 +- .../templates/_helpers.tpl | 16 +- .../templates/alerting.yaml | 18 +- .../{sentry => cairnobs}/templates/api.yaml | 20 +- .../templates/clickhouse.yaml | 20 +- .../templates/enterprise-api.yaml | 32 +-- .../templates/enterprise-auth.yaml | 16 +- .../templates/enterprise-ingest.yaml | 14 +- .../templates/ingest.yaml | 20 +- .../templates/postgres.yaml | 18 +- .../templates/redpanda.yaml | 20 +- .../templates/search.yaml | 20 +- .../templates/secrets.yaml | 18 +- .../templates/tenant-operator.yaml | 20 +- .../templates/tenants.yaml | 4 +- .../{sentry => cairnobs}/templates/web.yaml | 14 +- deploy/helm/{sentry => cairnobs}/values.yaml | 28 +-- deploy/operator/Dockerfile | 2 +- deploy/operator/README.md | 14 +- .../api/v1alpha1/groupversion_info.go | 6 +- deploy/operator/api/v1alpha1/tenant_types.go | 2 +- deploy/operator/cmd/tenant-operator/main.go | 6 +- ..._tenants.yaml => cairnobs.io_tenants.yaml} | 4 +- deploy/operator/go.mod | 2 +- .../internal/controller/tenant_controller.go | 18 +- .../controller/tenant_controller_test.go | 40 +-- docker-compose.yml | 68 ++--- docs/agent-heartbeat-monitoring.md | 14 +- docs/agent-management-design.md | 4 +- docs/architecture.md | 2 +- docs/compliance/license-audit-report.md | 29 +-- docs/compliance/license-policy.md | 2 +- docs/design-system.md | 4 +- docs/phase-0-runbook.md | 8 +- docs/phase-1-runbook.md | 18 +- docs/phase-2-runbook.md | 2 +- docs/phase-3-alerting-design.md | 4 +- docs/phase-3-dashboard-design.md | 4 +- docs/phase-4-runbook.md | 104 ++++---- docs/phase-7-ai-design.md | 2 +- docs/phase-7-runbook.md | 2 +- docs/query-language-reference.md | 22 +- docs/security/threat-model.md | 2 +- enterprise/README.md | 8 +- enterprise/cmd/enterprise-api/main.go | 34 +-- enterprise/cmd/enterprise-auth/main.go | 18 +- enterprise/cmd/enterprise-ingest/main.go | 8 +- enterprise/go.mod | 16 +- enterprise/internal/apiconfig/apiconfig.go | 2 +- .../internal/audit/agent_command_adapter.go | 4 +- .../internal/audit/ai_interaction_adapter.go | 4 +- enterprise/internal/audit/integration_test.go | 8 +- enterprise/internal/audit/queryapi_adapter.go | 4 +- .../internal/authhandler/authhandler.go | 6 +- .../internal/authhandler/authhandler_test.go | 2 +- enterprise/internal/chrunner/chrunner.go | 4 +- enterprise/internal/chrunner/chrunner_test.go | 6 +- enterprise/internal/chwriter/chwriter.go | 6 +- enterprise/internal/chwriter/chwriter_test.go | 10 +- enterprise/internal/config/config.go | 4 +- .../internal/groundingregistry/registry.go | 8 +- .../groundingregistry/registry_test.go | 4 +- .../internal/ingestconfig/ingestconfig.go | 2 +- .../internal/loginhandler/loginhandler.go | 16 +- .../loginhandler/loginhandler_test.go | 22 +- enterprise/internal/loginhandler/saml_test.go | 22 +- enterprise/internal/oidc/oidc.go | 2 +- enterprise/internal/oidc/oidc_test.go | 2 +- .../internal/rbacstore/dashboards_adapter.go | 4 +- .../internal/rbacstore/rbacstore_test.go | 6 +- enterprise/internal/saml/saml.go | 10 +- .../internal/searchclient/searchclient.go | 4 +- .../searchclient/searchclient_test.go | 4 +- enterprise/internal/tenantcrd/tenantcrd.go | 14 +- .../internal/tenantcrd/tenantcrd_test.go | 34 +-- .../tenantprovision/tenantprovision_test.go | 2 +- hack/alert-load-test/go.mod | 2 +- hack/benchmark-fixture/go.mod | 6 +- hack/benchmark-fixture/main.go | 2 +- hack/check-tenant-boundary.sh | 2 +- hack/dev-certs/generate.sh | 6 +- hack/mock-ollama/go.mod | 2 +- hack/webhook-sink/go.mod | 2 +- hack/windows-fixture/go.mod | 6 +- hack/windows-fixture/main.go | 2 +- ingest/clickhousewriter/writer.go | 4 +- ingest/cmd/ingest/main.go | 16 +- ingest/cmd/ingest/main_test.go | 4 +- ingest/consumer/consumer.go | 2 +- ingest/consumer/consumer_test.go | 2 +- ingest/go.mod | 6 +- .../internal/agentregistry/agentregistry.go | 2 +- ingest/internal/config/config.go | 8 +- ingest/internal/grpcserver/server.go | 6 +- ingest/internal/grpcserver/server_test.go | 6 +- ingest/internal/grpcserver/tls.go | 2 +- ingest/internal/normalize/normalize.go | 2 +- ingest/internal/normalize/normalize_test.go | 2 +- ingest/internal/producer/producer.go | 2 +- metadata/README.md | 4 +- metadata/docker-compose.yml | 4 +- proto/README.md | 4 +- proto/go.mod | 2 +- proto/sentry/agent/v1/agent_control.pb.go | 2 +- proto/sentry/agent/v1/agent_control.proto | 2 +- proto/sentry/logs/v1/logs.pb.go | 2 +- proto/sentry/logs/v1/logs.proto | 2 +- proto/sentry/search/v1/search.pb.go | 16 +- proto/sentry/search/v1/search.proto | 4 +- proto/sentry/search/v1/search_grpc.pb.go | 4 +- search/Cargo.lock | 38 +-- search/Cargo.toml | 6 +- search/Dockerfile | 6 +- search/README.md | 12 +- search/deny.toml | 2 +- search/src/config.rs | 8 +- search/src/main.rs | 2 +- storage/README.md | 4 +- storage/docker-compose.yml | 4 +- terraform/README.md | 98 ++++---- .../cairnobs_alert_rule/data-source.tf | 7 + .../cairnobs_dashboard/data-source.tf | 7 + .../data-source.tf | 8 +- .../sentry_alert_rule/data-source.tf | 7 - .../sentry_dashboard/data-source.tf | 7 - terraform/examples/provider/provider.tf | 16 +- .../resources/cairnobs_alert_rule/import.sh | 1 + .../resource.tf | 8 +- .../resources/cairnobs_dashboard/import.sh | 1 + .../resource.tf | 2 +- .../import.sh | 2 +- .../resource.tf | 8 +- .../cairnobs_notification_target/import.sh | 1 + .../resource.tf | 4 +- .../resources/sentry_alert_rule/import.sh | 1 - .../resources/sentry_dashboard/import.sh | 1 - .../sentry_notification_target/import.sh | 1 - terraform/go.mod | 2 +- .../provider/alert_rule_data_source.go | 2 +- .../provider/alert_rule_data_source_test.go | 14 +- .../internal/provider/alert_rule_resource.go | 6 +- .../provider/alert_rule_resource_test.go | 32 +-- terraform/internal/provider/client.go | 6 +- terraform/internal/provider/client_test.go | 2 +- .../provider/dashboard_data_source.go | 2 +- .../provider/dashboard_data_source_test.go | 14 +- .../provider/dashboard_panel_data_source.go | 4 +- .../dashboard_panel_data_source_test.go | 20 +- .../provider/dashboard_panel_resource.go | 12 +- .../provider/dashboard_panel_resource_test.go | 38 +-- .../internal/provider/dashboard_resource.go | 8 +- .../provider/dashboard_resource_test.go | 26 +- .../notification_target_data_source.go | 4 +- .../notification_target_data_source_test.go | 14 +- .../provider/notification_target_resource.go | 6 +- .../notification_target_resource_test.go | 24 +- terraform/internal/provider/provider.go | 50 ++-- terraform/internal/provider/provider_test.go | 58 ++--- terraform/main.go | 12 +- transport/Dockerfile | 2 +- transport/docker-compose.yml | 2 +- web/Dockerfile | 2 +- web/README.md | 6 +- web/src/app.html | 4 +- web/src/lib/api.ts | 10 +- web/src/lib/assets/favicon.svg | 10 +- web/src/lib/assets/logo-horizontal-dark.svg | 12 + web/src/lib/assets/logo-stacked-dark.svg | 10 + web/src/lib/charts/drilldown.ts | 2 +- web/src/lib/charts/setup.ts | 2 +- web/src/lib/components/CommandPalette.svelte | 2 +- web/src/lib/components/NavSidebar.svelte | 19 +- web/src/lib/density.svelte.ts | 2 +- web/src/lib/query-editor/language.ts | 2 +- web/src/lib/theme.svelte.ts | 2 +- web/src/routes/+layout.svelte | 6 +- web/src/routes/+page.svelte | 236 +++++------------- web/src/routes/search/+page.svelte | 204 +++++++++++++++ web/src/routes/search/+page.ts | 4 + web/src/routes/select-tenant/+page.svelte | 2 +- web/static/icons/favicon-16.png | Bin 0 -> 493 bytes web/static/icons/favicon-180.png | Bin 0 -> 5161 bytes web/static/icons/favicon-32.png | Bin 0 -> 912 bytes web/static/icons/favicon-48.png | Bin 0 -> 1392 bytes web/static/icons/favicon-512.png | Bin 0 -> 15615 bytes 291 files changed, 1565 insertions(+), 1441 deletions(-) rename agent/{sentry-agent => cairnobs-agent}/Cargo.toml (89%) rename agent/{sentry-agent => cairnobs-agent}/build.rs (100%) rename agent/{sentry-agent => cairnobs-agent}/config/agent.example.toml (78%) rename agent/{sentry-agent => cairnobs-agent}/src/batch.rs (100%) rename agent/{sentry-agent => cairnobs-agent}/src/config.rs (95%) rename agent/{sentry-agent => cairnobs-agent}/src/grpc.rs (100%) rename agent/{sentry-agent => cairnobs-agent}/src/main.rs (98%) rename agent/{sentry-agent => cairnobs-agent}/src/metrics.rs (100%) rename agent/{sentry-agent => cairnobs-agent}/src/service.rs (96%) rename agent/{sentry-agent => cairnobs-agent}/src/source/etw.rs (98%) rename agent/{sentry-agent => cairnobs-agent}/src/source/file_tail.rs (100%) rename agent/{sentry-agent => cairnobs-agent}/src/source/journald.rs (100%) rename agent/{sentry-agent => cairnobs-agent}/src/source/mod.rs (100%) rename agent/{sentry-agent => cairnobs-agent}/src/source/windows_eventlog.rs (99%) rename agent/{sentry-parser => cairnobs-parser}/Cargo.toml (86%) rename agent/{sentry-parser => cairnobs-parser}/src/lib.rs (100%) rename cli/cmd/{sentryctl => cairnobsctl}/auth_test.go (96%) rename cli/cmd/{sentryctl => cairnobsctl}/cmd_agents.go (82%) rename cli/cmd/{sentryctl => cairnobsctl}/cmd_agents_test.go (100%) rename cli/cmd/{sentryctl => cairnobsctl}/cmd_alerts.go (77%) rename cli/cmd/{sentryctl => cairnobsctl}/cmd_alerts_test.go (97%) rename cli/cmd/{sentryctl => cairnobsctl}/cmd_dashboards.go (72%) rename cli/cmd/{sentryctl => cairnobsctl}/cmd_dashboards_test.go (100%) rename cli/cmd/{sentryctl => cairnobsctl}/cmd_ping.go (93%) rename cli/cmd/{sentryctl => cairnobsctl}/cmd_query.go (98%) rename cli/cmd/{sentryctl => cairnobsctl}/cmd_query_test.go (100%) rename cli/cmd/{sentryctl => cairnobsctl}/cmd_users.go (89%) rename cli/cmd/{sentryctl => cairnobsctl}/cmd_users_test.go (98%) rename cli/cmd/{sentryctl => cairnobsctl}/httpclient.go (98%) rename cli/cmd/{sentryctl => cairnobsctl}/main.go (73%) rename cli/cmd/{sentryctl => cairnobsctl}/main_test.go (99%) rename deploy/helm/{sentry => cairnobs}/Chart.yaml (81%) rename deploy/helm/{sentry => cairnobs}/README.md (93%) rename deploy/helm/{sentry/crds/sentry.io_tenants.yaml => cairnobs/crds/cairnobs.io_tenants.yaml} (98%) rename deploy/helm/{sentry => cairnobs}/templates/_helpers.tpl (82%) rename deploy/helm/{sentry => cairnobs}/templates/alerting.yaml (77%) rename deploy/helm/{sentry => cairnobs}/templates/api.yaml (75%) rename deploy/helm/{sentry => cairnobs}/templates/clickhouse.yaml (82%) rename deploy/helm/{sentry => cairnobs}/templates/enterprise-api.yaml (83%) rename deploy/helm/{sentry => cairnobs}/templates/enterprise-auth.yaml (80%) rename deploy/helm/{sentry => cairnobs}/templates/enterprise-ingest.yaml (75%) rename deploy/helm/{sentry => cairnobs}/templates/ingest.yaml (81%) rename deploy/helm/{sentry => cairnobs}/templates/postgres.yaml (82%) rename deploy/helm/{sentry => cairnobs}/templates/redpanda.yaml (80%) rename deploy/helm/{sentry => cairnobs}/templates/search.yaml (69%) rename deploy/helm/{sentry => cairnobs}/templates/secrets.yaml (73%) rename deploy/helm/{sentry => cairnobs}/templates/tenant-operator.yaml (82%) rename deploy/helm/{sentry => cairnobs}/templates/tenants.yaml (75%) rename deploy/helm/{sentry => cairnobs}/templates/web.yaml (66%) rename deploy/helm/{sentry => cairnobs}/values.yaml (92%) rename deploy/operator/config/crd/{sentry.io_tenants.yaml => cairnobs.io_tenants.yaml} (98%) create mode 100644 terraform/examples/data-sources/cairnobs_alert_rule/data-source.tf create mode 100644 terraform/examples/data-sources/cairnobs_dashboard/data-source.tf rename terraform/examples/data-sources/{sentry_notification_target => cairnobs_notification_target}/data-source.tf (62%) delete mode 100644 terraform/examples/data-sources/sentry_alert_rule/data-source.tf delete mode 100644 terraform/examples/data-sources/sentry_dashboard/data-source.tf create mode 100644 terraform/examples/resources/cairnobs_alert_rule/import.sh rename terraform/examples/resources/{sentry_alert_rule => cairnobs_alert_rule}/resource.tf (71%) create mode 100644 terraform/examples/resources/cairnobs_dashboard/import.sh rename terraform/examples/resources/{sentry_dashboard => cairnobs_dashboard}/resource.tf (88%) rename terraform/examples/resources/{sentry_dashboard_panel => cairnobs_dashboard_panel}/import.sh (70%) rename terraform/examples/resources/{sentry_dashboard_panel => cairnobs_dashboard_panel}/resource.tf (66%) create mode 100644 terraform/examples/resources/cairnobs_notification_target/import.sh rename terraform/examples/resources/{sentry_notification_target => cairnobs_notification_target}/resource.tf (85%) delete mode 100644 terraform/examples/resources/sentry_alert_rule/import.sh delete mode 100644 terraform/examples/resources/sentry_dashboard/import.sh delete mode 100644 terraform/examples/resources/sentry_notification_target/import.sh create mode 100644 web/src/lib/assets/logo-horizontal-dark.svg create mode 100644 web/src/lib/assets/logo-stacked-dark.svg create mode 100644 web/src/routes/search/+page.svelte create mode 100644 web/src/routes/search/+page.ts create mode 100644 web/static/icons/favicon-16.png create mode 100644 web/static/icons/favicon-180.png create mode 100644 web/static/icons/favicon-32.png create mode 100644 web/static/icons/favicon-48.png create mode 100644 web/static/icons/favicon-512.png diff --git a/.github/workflows/license-compliance.yml b/.github/workflows/license-compliance.yml index 58479e1..205721a 100644 --- a/.github/workflows/license-compliance.yml +++ b/.github/workflows/license-compliance.yml @@ -63,7 +63,7 @@ jobs: run: | go-licenses check ./... \ --allowed_licenses=MIT,Apache-2.0,BSD-2-Clause,BSD-3-Clause,ISC,MPL-2.0,0BSD,Unlicense \ - --ignore github.com/sentry/sentry \ + --ignore github.com/cairnobs/cairnobs \ --ignore github.com/segmentio/asm npm-licenses: diff --git a/.gitignore b/.gitignore index 5a4eaa1..d433ed3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ search/target/ # binaries in-tree, but be defensive) /ingest/ingest /api/api -/cli/sentryctl +/cli/cairnobsctl /terraform/terraform-provider-sentry /alerting/alerting /hack/windows-fixture/windows-fixture diff --git a/CLAUDE.md b/CLAUDE.md index 4c3d424..5add6ce 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,4 +1,4 @@ -# Project: Sentry — Distributed Log Aggregation & Observability Platform +# Project: Cairn OBS — Distributed Log Aggregation & Observability Platform ## Mission Build an open-core, Kubernetes-native centralized logging platform that rivals @@ -22,19 +22,19 @@ described there without flagging it to me first. - Schema-on-write with OTel semantic conventions as the default schema, with schema-on-read fallback for unstructured text. - Every UI action must correspond to a documented REST/gRPC call. No - UI-only logic. CLI (`sentryctl`) and Terraform provider are first-class, - not afterthoughts. **Status**: `sentryctl` has been built out phase by + UI-only logic. CLI (`cairnobsctl`) and Terraform provider are first-class, + not afterthoughts. **Status**: `cairnobsctl` has been built out phase by phase since Phase 3. The Terraform provider (`/terraform`) only exists - as of this note -- four resources (`sentry_dashboard` and - `sentry_dashboard_panel`, both full CRUD, panels as their own resource + as of this note -- four resources (`cairnobs_dashboard` and + `cairnobs_dashboard_panel`, both full CRUD, panels as their own resource rather than a nested block since the API manages them independently - of their parent dashboard; `sentry_alert_rule` and - `sentry_notification_target`, both create/destroy only -- `alerting` + of their parent dashboard; `cairnobs_alert_rule` and + `cairnobs_notification_target`, both create/destroy only -- `alerting` has no `PUT /rules/{id}` or `PUT /targets/{id}` to update against), each paired with a read-only data source, built on HashiCorp's `terraform-plugin-framework`, reusing the exact same REST contracts - `sentryctl dashboards apply`/web's dashboard export and - `sentryctl alerts apply` already use. Tenant/RBAC resources are real, + `cairnobsctl dashboards apply`/web's dashboard export and + `cairnobsctl alerts apply` already use. Tenant/RBAC resources are real, disclosed future work -- see `/terraform/README.md` for the full accounting of what is and isn't built, and the same @@ -94,7 +94,7 @@ prominently in `/agent/README.md` and the runbook. ## What "done" looks like for Phase 2 -A single query bar in the web UI and a single `sentryctl query` command +A single query bar in the web UI and a single `cairnobsctl query` command can express filter + free-text + stats in one query (e.g. `service=api | where status>=500 | stats count by host | sort -count`, or `message:"connection refused" | stats count by host`), execute correctly @@ -212,7 +212,7 @@ the fix verified Docker-free (`chrunner_test.go`'s and tests) — see `api/queryapi/tenant_isolation_gap_test.go` for the full accounting of all four probes, now all closed. The deployment- topology gap that briefly was the largest one is now closed for both -Helm and docker-compose: `deploy/helm/sentry/templates/api.yaml`/ +Helm and docker-compose: `deploy/helm/cairnobs/templates/api.yaml`/ `enterprise-api.yaml` are mutually exclusive on the same `enterprise.enabled` flag that turns on RBAC/audit/SSO, rendering to the same Service name/port either way — a Helm-deployed cluster can't @@ -234,7 +234,7 @@ stricter still (creator/Admin/Owner only, closing a self-escalation path). Verified against a fake store (`api/dashboards/handler_test.go`); real integration tests exist but haven't run against a live Postgres, same disclosed gap as the rest of this phase's Postgres-backed pieces. -`sentryctl dashboards permissions list|grant|revoke` is now the CLI +`cairnobsctl dashboards permissions list|grant|revoke` is now the CLI surface for this — `PUT`/`DELETE /dashboards/{id}/permissions/{userId}` previously had no caller but Go tests and curl. `deploy/operator`'s `Tenant` CRD and `enterprise-api -provision-tenant` @@ -483,19 +483,19 @@ phase); CI enforcement wired up and every command verified locally. **The one real flag — Redpanda's BSL 1.1 license (confirmed against primary sources for the pinned v24.2.7, not assumed to still be Apache-2.0) — is resolved, not outstanding**: decision recorded -2026-08-16, accept as-is. Sentry's own use (internal Kafka-protocol +2026-08-16, accept as-is. Cairn OBS's own use (internal Kafka-protocol transport, no resale of broker access) sits within BSL's Additional Use -Grant; the harder question — whether a third party self-hosting Sentry +Grant; the harder question — whether a third party self-hosting Cairn OBS "as a service" using the bundled `docker-compose.yml` could trip BSL's anti-resale restriction on Redpanda specifically — was judged unlikely -given Sentry's ingest pipeline creates fixed internal topics, not +given Cairn OBS's ingest pipeline creates fixed internal topics, not per-end-user ones, and was accepted as a disclosed, known risk rather than triggering a swap to Apache Kafka (real resource-footprint cost) or dropping the bundled broker image (rougher local dev experience). See the audit report's Redpanda section for the full reasoning, the other two options that were considered and not chosen, and the condition under -which this should be revisited (an official hosted/managed Sentry -offering, which would make the third-party-SaaS scenario Sentry's own +which this should be revisited (an official hosted/managed Cairn OBS +offering, which would make the third-party-SaaS scenario Cairn OBS's own rather than a hypothetical one). Non-goals for this phase: replacing permissively-licensed dependencies diff --git a/agent/Cargo.lock b/agent/Cargo.lock index bd3169b..9ac5b14 100644 --- a/agent/Cargo.lock +++ b/agent/Cargo.lock @@ -177,6 +177,31 @@ version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" +[[package]] +name = "cairnobs-agent" +version = "0.1.0" +dependencies = [ + "anyhow", + "cairnobs-parser", + "clap", + "prost", + "quick-xml", + "serde", + "serde_json", + "tokio", + "toml", + "tonic", + "tonic-build", + "tracing", + "tracing-subscriber", + "windows", + "windows-service", +] + +[[package]] +name = "cairnobs-parser" +version = "0.1.0" + [[package]] name = "cc" version = "1.4.2" @@ -895,31 +920,6 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" -[[package]] -name = "sentry-agent" -version = "0.1.0" -dependencies = [ - "anyhow", - "clap", - "prost", - "quick-xml", - "sentry-parser", - "serde", - "serde_json", - "tokio", - "toml", - "tonic", - "tonic-build", - "tracing", - "tracing-subscriber", - "windows", - "windows-service", -] - -[[package]] -name = "sentry-parser" -version = "0.1.0" - [[package]] name = "serde" version = "1.0.229" diff --git a/agent/Cargo.toml b/agent/Cargo.toml index 48b649f..951701b 100644 --- a/agent/Cargo.toml +++ b/agent/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["sentry-parser", "sentry-agent"] +members = ["cairnobs-parser", "cairnobs-agent"] [workspace.package] version = "0.1.0" diff --git a/agent/Dockerfile b/agent/Dockerfile index f7355e6..f84ee00 100644 --- a/agent/Dockerfile +++ b/agent/Dockerfile @@ -1,6 +1,6 @@ # Build context must be the repo root (sentry/), not agent/, since this # needs both agent/ and proto/: -# docker build -f agent/Dockerfile -t sentry-agent . +# docker build -f agent/Dockerfile -t cairnobs-agent . FROM rust:1-alpine AS builder RUN apk add --no-cache musl-dev protobuf-dev protobuf @@ -14,8 +14,8 @@ WORKDIR /src/agent # alongside the journald default rather than left as an opt-in most # deployments would otherwise have to remember to ask for. RUN rustup target add x86_64-unknown-linux-musl \ - && cargo build --release --target x86_64-unknown-linux-musl -p sentry-agent --features journald,file-tail + && cargo build --release --target x86_64-unknown-linux-musl -p cairnobs-agent --features journald,file-tail FROM scratch -COPY --from=builder /src/agent/target/x86_64-unknown-linux-musl/release/sentry-agent /sentry-agent -ENTRYPOINT ["/sentry-agent"] +COPY --from=builder /src/agent/target/x86_64-unknown-linux-musl/release/cairnobs-agent /cairnobs-agent +ENTRYPOINT ["/cairnobs-agent"] diff --git a/agent/README.md b/agent/README.md index 59de471..6b1af5b 100644 --- a/agent/README.md +++ b/agent/README.md @@ -1,4 +1,4 @@ -# sentry-agent +# cairnobs-agent Distro-agnostic Linux/Windows log collector. On Linux, statically linked against musl, no glibc runtime dependency. Tails journald (Linux default), @@ -17,9 +17,9 @@ real before trusting it. See `/docs/phase-1-runbook.md`. ## Workspace layout -- `sentry-parser` — pure-`std` RFC 5424 syslog parser with raw-passthrough +- `cairnobs-parser` — pure-`std` RFC 5424 syslog parser with raw-passthrough fallback. No I/O, easy to unit test in isolation. -- `sentry-agent` — the binary: config loading, sourcing (journald/file/ +- `cairnobs-agent` — the binary: config loading, sourcing (journald/file/ Windows Event Log/ETW), batching, mTLS gRPC client, Windows service wrapper. @@ -75,7 +75,7 @@ Container build (see caveat below): ```sh # from the repo root, not agent/ -docker build -f agent/Dockerfile -t sentry-agent . +docker build -f agent/Dockerfile -t cairnobs-agent . ``` **Caveat:** the container image is provided for CI/completeness, but @@ -117,11 +117,11 @@ automatable vs. manual-only. No CLI flags are required for the common case: ```sh -./sentry-agent +./cairnobs-agent ``` This uses the platform's conventional config path if present -(`/etc/sentry-agent/agent.toml` on Linux, `C:\ProgramData\SentryAgent\agent.toml` +(`/etc/cairnobs-agent/agent.toml` on Linux, `C:\ProgramData\CairnObsAgent\agent.toml` on Windows), otherwise built-in defaults: journald source on Linux (whole journal, no unit filter), service name `default`, and mTLS material expected under the same conventional directory @@ -132,7 +132,7 @@ fail fast with a clear error rather than connecting insecurely. See `config/agent.example.toml` for all fields. ```sh -./sentry-agent --config /path/to/agent.toml +./cairnobs-agent --config /path/to/agent.toml ``` ## Heartbeat and unavailability alerting @@ -142,7 +142,7 @@ schedule (`[heartbeat]` in the config, default every 60s), separate from whatever real log traffic is flowing — see `config/agent.example.toml`. This isn't a new wire protocol: it's an ordinary record through the same `PushBatch` RPC and mTLS identity every log line uses, tagged with a -`sentry.heartbeat=true` attribute so it's easy to filter for and doesn't +`cairnobs.heartbeat=true` attribute so it's easy to filter for and doesn't show up as noise in normal log views. Set `interval` to a plain number plus `s`/`m`/`h` (matches the query language's own `earliest=`/`latest=` units); `enabled = false` turns it off entirely. @@ -157,7 +157,7 @@ to create. Same shape as heartbeat, same reasoning: `[metrics]` in the config (`enabled = false` by default) sends a periodic record — CPU%, memory -used/total, disk used/total for `/` — tagged `sentry.metrics=true`, with +used/total, disk used/total for `/` — tagged `cairnobs.metrics=true`, with the individual numbers as their own attributes (`cpu_percent`, `mem_used_bytes`, `mem_total_bytes`, `disk_used_bytes`, `disk_total_bytes`), queryable directly (e.g. `cpu_percent > 80`) since @@ -169,7 +169,7 @@ dependencies, same "shell out to a boring, ubiquitous tool" precedent `journalctl` already sets. **Enable this on only one agent process per physical host.** It's -common for one host to run several `sentry-agent` processes (one per +common for one host to run several `cairnobs-agent` processes (one per log source, each needing its own `[agent] host` value to work around the `agents` table's `UNIQUE (tenant_id, host)` constraint — see `/docs/agent-management-design.md`) — turning `[metrics]` on for more @@ -185,14 +185,14 @@ console — that's what `service.rs` (via the `windows-service` crate) does. From an administrator shell: ```powershell -sentry-agent.exe install # registers the service, Automatic start, LocalSystem account -sc.exe start SentryAgent -sc.exe stop SentryAgent -sentry-agent.exe uninstall +cairnobs-agent.exe install # registers the service, Automatic start, LocalSystem account +sc.exe start CairnObsAgent +sc.exe stop CairnObsAgent +cairnobs-agent.exe uninstall ``` `install`/`uninstall`/`run-service` are subcommands only present in -Windows builds (`sentry-agent` with no subcommand is still the normal +Windows builds (`cairnobs-agent` with no subcommand is still the normal foreground/console run, same as on Linux) — `run-service` specifically is what the SCM itself invokes at service start; don't run it directly. @@ -224,7 +224,7 @@ about since they're very different amounts of work: 1. **What this repo supports today, with zero extra code:** WEF is a native Windows-to-Windows mechanism (`wecsvc`, the built-in Windows Event Collector role) — endpoints forward to a Windows Server acting - as collector using Windows' own mechanism, no Sentry code involved in + as collector using Windows' own mechanism, no Cairn OBS code involved in the forwarding itself. Run this agent *on the collector box*, subscribed to the `ForwardedEvents` channel instead of the usual three: ```toml @@ -233,9 +233,9 @@ about since they're very different amounts of work: channels = ["ForwardedEvents"] ``` 2. **What this repo does *not* implement:** a true agentless receiver — - Sentry itself speaking the WS-Management/WinRM event-subscription + Cairn OBS itself speaking the WS-Management/WinRM event-subscription protocol so endpoints can forward directly to `ingest` without any - Windows Event Collector role or Sentry agent anywhere. That's a + Windows Event Collector role or Cairn OBS agent anywhere. That's a standalone protocol implementation (SOAP-ish subscription/heartbeat/ delivery over WinRM), not an agent or ingest-side tweak, and it's out of scope for Phase 1. If you need this, it's a real project of its diff --git a/agent/sentry-agent/Cargo.toml b/agent/cairnobs-agent/Cargo.toml similarity index 89% rename from agent/sentry-agent/Cargo.toml rename to agent/cairnobs-agent/Cargo.toml index b6f595b..f132868 100644 --- a/agent/sentry-agent/Cargo.toml +++ b/agent/cairnobs-agent/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "sentry-agent" +name = "cairnobs-agent" version.workspace = true edition.workspace = true license.workspace = true -description = "Sentry distro-agnostic Linux/Windows log collector" +description = "Cairn OBS distro-agnostic Linux/Windows log collector" [[bin]] -name = "sentry-agent" +name = "cairnobs-agent" path = "src/main.rs" [features] @@ -21,7 +21,7 @@ windows-eventlog = [] etw = [] [dependencies] -sentry-parser = { path = "../sentry-parser" } +cairnobs-parser = { path = "../cairnobs-parser" } tokio = { version = "1", features = ["rt-multi-thread", "macros", "process", "io-util", "io-std", "time", "fs", "sync", "signal"] } tonic = { version = "0.12", features = ["tls"] } diff --git a/agent/sentry-agent/build.rs b/agent/cairnobs-agent/build.rs similarity index 100% rename from agent/sentry-agent/build.rs rename to agent/cairnobs-agent/build.rs diff --git a/agent/sentry-agent/config/agent.example.toml b/agent/cairnobs-agent/config/agent.example.toml similarity index 78% rename from agent/sentry-agent/config/agent.example.toml rename to agent/cairnobs-agent/config/agent.example.toml index fac537d..879791e 100644 --- a/agent/sentry-agent/config/agent.example.toml +++ b/agent/cairnobs-agent/config/agent.example.toml @@ -1,12 +1,12 @@ -# Example sentry-agent config. Copy to the platform's conventional path -# (/etc/sentry-agent/agent.toml on Linux, C:\ProgramData\SentryAgent\agent.toml +# Example cairnobs-agent config. Copy to the platform's conventional path +# (/etc/cairnobs-agent/agent.toml on Linux, C:\ProgramData\CairnObsAgent\agent.toml # on Windows), or pass --config /path/to/this/file. # # Every field has a built-in default (see src/config.rs), so this file only # needs to contain what you're overriding. An agent with NO config file at # all still runs: on Linux it defaults to journald, service = "default", -# and expects mTLS material at /etc/sentry-agent/{ca,client,client-key}.pem -# (Windows equivalents under C:\ProgramData\SentryAgent\). +# and expects mTLS material at /etc/cairnobs-agent/{ca,client,client-key}.pem +# (Windows equivalents under C:\ProgramData\CairnObsAgent\). [agent] # host = "explicit-hostname-override" # defaults to /etc/hostname (Linux) or %COMPUTERNAME% (Windows) @@ -41,7 +41,7 @@ flush_interval_ms = 2000 [heartbeat] # How often this agent proves it's still alive to the platform, sent as # its own record independent of whatever real log traffic is flowing -- -# pair with an "absence" alert rule on the sentry.heartbeat attribute to +# pair with an "absence" alert rule on the cairnobs.heartbeat attribute to # get paged when a host goes quiet. Accepts a plain number + unit: s # (seconds), m (minutes), or h (hours) -- same vocabulary as # earliest=/latest= in the query language. See @@ -55,7 +55,7 @@ interval = "60s" # Host CPU/memory/disk usage, sent as its own periodic record the same # way heartbeat is (see web/'s "Hosts" nav section). Off by default -- # unlike heartbeat, this is a deliberate per-host decision: if several -# sentry-agent processes run on the same physical host (e.g. one per log +# cairnobs-agent processes run on the same physical host (e.g. one per log # source), enable this on only ONE of them, or the same host will report # multiple conflicting metric series. Linux-only for now. Root disk ("/") # only -- not configurable in this release. @@ -66,6 +66,6 @@ interval = "60s" endpoint = "https://ingest.internal:4317" [tls] -ca_cert = "/etc/sentry-agent/ca.pem" -client_cert = "/etc/sentry-agent/client.pem" -client_key = "/etc/sentry-agent/client-key.pem" +ca_cert = "/etc/cairnobs-agent/ca.pem" +client_cert = "/etc/cairnobs-agent/client.pem" +client_key = "/etc/cairnobs-agent/client-key.pem" diff --git a/agent/sentry-agent/src/batch.rs b/agent/cairnobs-agent/src/batch.rs similarity index 100% rename from agent/sentry-agent/src/batch.rs rename to agent/cairnobs-agent/src/batch.rs diff --git a/agent/sentry-agent/src/config.rs b/agent/cairnobs-agent/src/config.rs similarity index 95% rename from agent/sentry-agent/src/config.rs rename to agent/cairnobs-agent/src/config.rs index df7e253..973ef6c 100644 --- a/agent/sentry-agent/src/config.rs +++ b/agent/cairnobs-agent/src/config.rs @@ -4,9 +4,9 @@ use std::path::{Path, PathBuf}; use std::time::Duration; #[cfg(not(windows))] -const DEFAULT_CONFIG_PATH: &str = "/etc/sentry-agent/agent.toml"; +const DEFAULT_CONFIG_PATH: &str = "/etc/cairnobs-agent/agent.toml"; #[cfg(windows)] -const DEFAULT_CONFIG_PATH: &str = r"C:\ProgramData\SentryAgent\agent.toml"; +const DEFAULT_CONFIG_PATH: &str = r"C:\ProgramData\CairnObsAgent\agent.toml"; #[derive(Debug, Clone, Deserialize, Default)] #[serde(default)] @@ -23,8 +23,8 @@ pub struct Config { impl Config { /// Loads config from `explicit_path` if given, else from the /// platform's conventional config path if it exists - /// (`/etc/sentry-agent/agent.toml` on Linux, - /// `C:\ProgramData\SentryAgent\agent.toml` on Windows), else falls + /// (`/etc/cairnobs-agent/agent.toml` on Linux, + /// `C:\ProgramData\CairnObsAgent\agent.toml` on Windows), else falls /// back to built-in defaults (journald source on Linux, default TLS /// cert paths). Only an explicitly-passed `--config` path that doesn't /// exist is an error; the conventional default path is optional. @@ -150,7 +150,7 @@ impl Default for BatchConfig { /// `flush_interval_ms` like real records do. This is the operator-facing /// "polling resolution" knob: how often this agent proves it's still /// alive, which a `condition_type = "absence"` alert rule on the -/// `sentry.heartbeat` attribute (see /docs/agent-heartbeat-monitoring.md) +/// `cairnobs.heartbeat` attribute (see /docs/agent-heartbeat-monitoring.md) /// turns into "alert when this host goes quiet." #[derive(Debug, Clone, Deserialize)] #[serde(default)] @@ -321,10 +321,10 @@ impl Default for TlsConfig { #[cfg(not(windows))] fn default_cert_path(name: &str) -> PathBuf { - PathBuf::from(format!("/etc/sentry-agent/{name}")) + PathBuf::from(format!("/etc/cairnobs-agent/{name}")) } #[cfg(windows)] fn default_cert_path(name: &str) -> PathBuf { - PathBuf::from(format!(r"C:\ProgramData\SentryAgent\{name}")) + PathBuf::from(format!(r"C:\ProgramData\CairnObsAgent\{name}")) } diff --git a/agent/sentry-agent/src/grpc.rs b/agent/cairnobs-agent/src/grpc.rs similarity index 100% rename from agent/sentry-agent/src/grpc.rs rename to agent/cairnobs-agent/src/grpc.rs diff --git a/agent/sentry-agent/src/main.rs b/agent/cairnobs-agent/src/main.rs similarity index 98% rename from agent/sentry-agent/src/main.rs rename to agent/cairnobs-agent/src/main.rs index 9a30ec0..d3e9ef2 100644 --- a/agent/sentry-agent/src/main.rs +++ b/agent/cairnobs-agent/src/main.rs @@ -31,7 +31,7 @@ use tokio::sync::mpsc; use tonic::transport::Channel; #[derive(Parser)] -#[command(name = "sentry-agent", about = "Sentry Linux/Windows log collector")] +#[command(name = "cairnobs-agent", about = "Cairn OBS Linux/Windows log collector")] struct Cli { /// Path to a TOML config file. Defaults to the platform's conventional /// path if present, otherwise built-in defaults — see config::Config::load. @@ -53,7 +53,7 @@ enum WindowsCommand { Uninstall, /// Entry point the Service Control Manager invokes when starting the /// registered service. Not meant to be run directly by a user — use - /// `sentry-agent` with no subcommand for a normal foreground/console + /// `cairnobs-agent` with no subcommand for a normal foreground/console /// run, same as on Linux. RunService, } @@ -244,7 +244,7 @@ pub async fn run_agent(config_path: Option) -> Result<()> { tracing::warn!("source exited, flushing remaining batch and shutting down"); break; }; - let parsed = sentry_parser::parse(&raw.line); + let parsed = cairnobs_parser::parse(&raw.line); let severity = to_pb_severity(raw.severity_hint.or(parsed.severity)); let mut attributes: std::collections::HashMap = parsed.attributes.into_iter().collect(); @@ -476,7 +476,7 @@ async fn spawn_source(source: config::SourceConfig, tx: source::LineSender) { /// ticker's own comment above): a heartbeat that got queued behind /// `batch.max_size` or `batch.flush_interval_ms` would defeat the point /// of a punctual "still alive" signal. Distinguished from a real log -/// record purely by the `sentry.heartbeat` attribute -- `service` stays +/// record purely by the `cairnobs.heartbeat` attribute -- `service` stays /// the agent's real configured service so it doesn't pollute /// service-based dashboards/faceting with a fake value. See /// /docs/agent-heartbeat-monitoring.md for how an absence alert rule @@ -488,7 +488,7 @@ async fn send_heartbeat(client: &mut LogIngestClient, host: &str, servi service: service.to_string(), severity: Severity::Info as i32, message: "agent heartbeat".to_string(), - attributes: std::collections::HashMap::from([("sentry.heartbeat".to_string(), "true".to_string())]), + attributes: std::collections::HashMap::from([("cairnobs.heartbeat".to_string(), "true".to_string())]), record_id: String::new(), }; match grpc::send_batch(client, format!("heartbeat-{}", batch_id()), vec![record]).await { @@ -499,7 +499,7 @@ async fn send_heartbeat(client: &mut LogIngestClient, host: &str, servi /// Same "no new proto, no new ingest code, no new ClickHouse schema" /// shape as `send_heartbeat` above -- a metrics sample is just another -/// tagged `LogRecord`, distinguished by the `sentry.metrics` attribute. +/// tagged `LogRecord`, distinguished by the `cairnobs.metrics` attribute. /// Unlike heartbeat, the numeric fields themselves are real query-language /// attributes too (`cpu_percent`, `mem_used_bytes`, etc.) rather than /// being folded into `message` -- confirmed before building this that @@ -523,7 +523,7 @@ async fn send_metrics(client: &mut LogIngestClient, host: &str, service severity: Severity::Info as i32, message: "host metrics".to_string(), attributes: std::collections::HashMap::from([ - ("sentry.metrics".to_string(), "true".to_string()), + ("cairnobs.metrics".to_string(), "true".to_string()), ("cpu_percent".to_string(), format!("{:.2}", m.cpu_percent)), ("mem_used_bytes".to_string(), m.mem_used_bytes.to_string()), ("mem_total_bytes".to_string(), m.mem_total_bytes.to_string()), diff --git a/agent/sentry-agent/src/metrics.rs b/agent/cairnobs-agent/src/metrics.rs similarity index 100% rename from agent/sentry-agent/src/metrics.rs rename to agent/cairnobs-agent/src/metrics.rs diff --git a/agent/sentry-agent/src/service.rs b/agent/cairnobs-agent/src/service.rs similarity index 96% rename from agent/sentry-agent/src/service.rs rename to agent/cairnobs-agent/src/service.rs index 463ac7e..5593f24 100644 --- a/agent/sentry-agent/src/service.rs +++ b/agent/cairnobs-agent/src/service.rs @@ -27,12 +27,12 @@ use windows_service::service_control_handler::{self, ServiceControlHandlerResult use windows_service::service_manager::{ServiceManager, ServiceManagerAccess}; use windows_service::{define_windows_service, service_dispatcher}; -pub const SERVICE_NAME: &str = "SentryAgent"; +pub const SERVICE_NAME: &str = "CairnObsAgent"; const SERVICE_TYPE: ServiceType = ServiceType::OWN_PROCESS; /// Registers this binary as a Windows service: Automatic start, /// LocalSystem account, invoked with the `run-service` subcommand (which -/// is what the SCM actually launches — not a bare `sentry-agent` with no +/// is what the SCM actually launches — not a bare `cairnobs-agent` with no /// arguments). Requires an administrator shell. pub fn install() -> Result<()> { let manager = ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CREATE_SERVICE) @@ -42,7 +42,7 @@ pub fn install() -> Result<()> { let service_info = ServiceInfo { name: OsString::from(SERVICE_NAME), - display_name: OsString::from("Sentry Log Agent"), + display_name: OsString::from("Cairn OBS Log Agent"), service_type: SERVICE_TYPE, start_type: ServiceStartType::AutoStart, error_control: ServiceErrorControl::Normal, @@ -57,7 +57,7 @@ pub fn install() -> Result<()> { .create_service(&service_info, ServiceAccess::CHANGE_CONFIG) .context("creating service")?; service - .set_description("Ships local logs to Sentry ingest over mTLS.") + .set_description("Ships local logs to Cairn OBS ingest over mTLS.") .context("setting service description")?; tracing::info!(service = SERVICE_NAME, "installed Windows service"); diff --git a/agent/sentry-agent/src/source/etw.rs b/agent/cairnobs-agent/src/source/etw.rs similarity index 98% rename from agent/sentry-agent/src/source/etw.rs rename to agent/cairnobs-agent/src/source/etw.rs index ac0401c..9bef503 100644 --- a/agent/sentry-agent/src/source/etw.rs +++ b/agent/cairnobs-agent/src/source/etw.rs @@ -44,7 +44,7 @@ use windows::Win32::System::Diagnostics::Etw::{ PROCESS_TRACE_MODE_REAL_TIME, TRACE_LEVEL_VERBOSE, }; -const SESSION_NAME: &str = "SentryAgentEtw"; +const SESSION_NAME: &str = "CairnObsAgentEtw"; pub async fn run(providers: &[String], tx: LineSender) -> Result<()> { let providers = providers.to_vec(); @@ -212,7 +212,7 @@ unsafe extern "system" fn event_record_callback(record: *mut EVENT_RECORD) { // No TDH-based message rendering (see module doc comment) -- this is // a coarse, structured summary rather than a human-authored message. - // Downstream (sentry_parser's raw-passthrough fallback) handles a + // Downstream (cairnobs_parser's raw-passthrough fallback) handles a // non-RFC5424 line like this the same as any other raw line. let message = format!( "ETW event: provider={:?} id={} level={}", diff --git a/agent/sentry-agent/src/source/file_tail.rs b/agent/cairnobs-agent/src/source/file_tail.rs similarity index 100% rename from agent/sentry-agent/src/source/file_tail.rs rename to agent/cairnobs-agent/src/source/file_tail.rs diff --git a/agent/sentry-agent/src/source/journald.rs b/agent/cairnobs-agent/src/source/journald.rs similarity index 100% rename from agent/sentry-agent/src/source/journald.rs rename to agent/cairnobs-agent/src/source/journald.rs diff --git a/agent/sentry-agent/src/source/mod.rs b/agent/cairnobs-agent/src/source/mod.rs similarity index 100% rename from agent/sentry-agent/src/source/mod.rs rename to agent/cairnobs-agent/src/source/mod.rs diff --git a/agent/sentry-agent/src/source/windows_eventlog.rs b/agent/cairnobs-agent/src/source/windows_eventlog.rs similarity index 99% rename from agent/sentry-agent/src/source/windows_eventlog.rs rename to agent/cairnobs-agent/src/source/windows_eventlog.rs index d94789b..a63e7b8 100644 --- a/agent/sentry-agent/src/source/windows_eventlog.rs +++ b/agent/cairnobs-agent/src/source/windows_eventlog.rs @@ -226,7 +226,7 @@ fn parse_event_xml(xml: &str, channel: &str) -> Option { // rendered properly. Real message-template rendering needs // EvtFormatMessage against the provider's message-table resource -- // worth a follow-up, not required for a raw-passthrough-shaped record - // (sentry_parser's raw fallback handles this fine either way). + // (cairnobs_parser's raw fallback handles this fine either way). let message = if event_data_values.is_empty() { xml.to_string() } else { diff --git a/agent/sentry-parser/Cargo.toml b/agent/cairnobs-parser/Cargo.toml similarity index 86% rename from agent/sentry-parser/Cargo.toml rename to agent/cairnobs-parser/Cargo.toml index 2976226..e28dce2 100644 --- a/agent/sentry-parser/Cargo.toml +++ b/agent/cairnobs-parser/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "sentry-parser" +name = "cairnobs-parser" version.workspace = true edition.workspace = true license.workspace = true diff --git a/agent/sentry-parser/src/lib.rs b/agent/cairnobs-parser/src/lib.rs similarity index 100% rename from agent/sentry-parser/src/lib.rs rename to agent/cairnobs-parser/src/lib.rs diff --git a/agent/deny.toml b/agent/deny.toml index e6a45df..9517449 100644 --- a/agent/deny.toml +++ b/agent/deny.toml @@ -88,7 +88,7 @@ ignore = [ # List of explicitly allowed licenses # See https://spdx.org/licenses/ for list of possible licenses # [possible values: any SPDX 3.11 short identifier (+ optional exception)]. -# Sentry's own AGPLv3-project license policy (Phase 6 license audit -- +# Cairn OBS's own AGPLv3-project license policy (Phase 6 license audit -- # see /docs/compliance/license-policy.md for the full rationale per # category). Every license actually found in this crate's dependency # tree at audit time is listed explicitly here, not just the common diff --git a/alerting/README.md b/alerting/README.md index 9dc0549..480a47f 100644 --- a/alerting/README.md +++ b/alerting/README.md @@ -8,7 +8,7 @@ correctness properties this implementation follows exactly). ## Running ```sh -POSTGRES_PASSWORD=sentry-dev-only API_QUERY_URL=http://localhost:8080 go run ./cmd/alerting +POSTGRES_PASSWORD=cairnobs-dev-only API_QUERY_URL=http://localhost:8080 go run ./cmd/alerting ``` Talks to the same `sentry_metadata` Postgres database as `/api` diff --git a/alerting/cmd/alerting/main.go b/alerting/cmd/alerting/main.go index 3cbb19e..83df2eb 100644 --- a/alerting/cmd/alerting/main.go +++ b/alerting/cmd/alerting/main.go @@ -1,9 +1,9 @@ -// Command alerting is Sentry's alert rule evaluator and delivery +// Command alerting is Cairn OBS's alert rule evaluator and delivery // service: rule/target CRUD, the ticker-driven ok/pending/firing // evaluator, and the webhook/Slack/PagerDuty delivery worker. See // /docs/phase-3-alerting-design.md. Never talks to ClickHouse/Tantivy // directly -- rule queries run through /api's POST /query -// (internal/queryclient), same precedent sentryctl query and the web +// (internal/queryclient), same precedent cairnobsctl query and the web // UI's dashboard panels already set. package main @@ -21,15 +21,15 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "golang.org/x/sync/errgroup" - "github.com/sentry/sentry/alerting/internal/config" - "github.com/sentry/sentry/alerting/internal/delivery" - "github.com/sentry/sentry/alerting/internal/evaluator" - "github.com/sentry/sentry/alerting/internal/httpapi" - "github.com/sentry/sentry/alerting/internal/httpserver" - "github.com/sentry/sentry/alerting/internal/notifystore" - "github.com/sentry/sentry/alerting/internal/queryclient" - "github.com/sentry/sentry/alerting/internal/rulestore" - "github.com/sentry/sentry/alerting/internal/sessioncheck" + "github.com/cairnobs/cairnobs/alerting/internal/config" + "github.com/cairnobs/cairnobs/alerting/internal/delivery" + "github.com/cairnobs/cairnobs/alerting/internal/evaluator" + "github.com/cairnobs/cairnobs/alerting/internal/httpapi" + "github.com/cairnobs/cairnobs/alerting/internal/httpserver" + "github.com/cairnobs/cairnobs/alerting/internal/notifystore" + "github.com/cairnobs/cairnobs/alerting/internal/queryclient" + "github.com/cairnobs/cairnobs/alerting/internal/rulestore" + "github.com/cairnobs/cairnobs/alerting/internal/sessioncheck" ) func main() { diff --git a/alerting/go.mod b/alerting/go.mod index aa3faaa..04be622 100644 --- a/alerting/go.mod +++ b/alerting/go.mod @@ -1,4 +1,4 @@ -module github.com/sentry/sentry/alerting +module github.com/cairnobs/cairnobs/alerting go 1.25.0 diff --git a/alerting/internal/config/config.go b/alerting/internal/config/config.go index c53ab1a..42ad5e0 100644 --- a/alerting/internal/config/config.go +++ b/alerting/internal/config/config.go @@ -53,7 +53,7 @@ type EvaluatorConfig struct { // api/internal/config.Config.DevCredentialWarnings for the full // reasoning (duplicated here per this repo's no-shared-code-between- // services convention). -const devOnlyCredential = "sentry-dev-only" +const devOnlyCredential = "cairnobs-dev-only" // DevCredentialWarnings reports whether the configured Postgres // credential still equals the literal dev-only default -- diff --git a/alerting/internal/delivery/delivery_test.go b/alerting/internal/delivery/delivery_test.go index 726e05f..58ab28b 100644 --- a/alerting/internal/delivery/delivery_test.go +++ b/alerting/internal/delivery/delivery_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/sentry/sentry/alerting/internal/notifystore" + "github.com/cairnobs/cairnobs/alerting/internal/notifystore" ) func TestBuildPayloadGenericDefaultShape(t *testing.T) { diff --git a/alerting/internal/delivery/pagerduty.go b/alerting/internal/delivery/pagerduty.go index 7307eb8..92b10a3 100644 --- a/alerting/internal/delivery/pagerduty.go +++ b/alerting/internal/delivery/pagerduty.go @@ -4,7 +4,7 @@ import ( "encoding/json" "fmt" - "github.com/sentry/sentry/alerting/internal/notifystore" + "github.com/cairnobs/cairnobs/alerting/internal/notifystore" ) // pagerDutyPayload is PagerDuty's Events API v2 shape. routing_key comes @@ -50,7 +50,7 @@ func buildPagerDutyPayload(target notifystore.Target, event Event) ([]byte, erro DedupKey: event.RuleID, Payload: pagerDutyEventPayload{ Summary: summary, - Source: "sentry", + Source: "cairnobs", Severity: severity, }, }) diff --git a/alerting/internal/delivery/webhook.go b/alerting/internal/delivery/webhook.go index 7031532..b640ef8 100644 --- a/alerting/internal/delivery/webhook.go +++ b/alerting/internal/delivery/webhook.go @@ -19,7 +19,7 @@ import ( "github.com/jackc/pgx/v5/pgxpool" - "github.com/sentry/sentry/alerting/internal/notifystore" + "github.com/cairnobs/cairnobs/alerting/internal/notifystore" ) // Event is the firing/resolved occurrence a payload is rendered from. diff --git a/alerting/internal/evaluator/condition.go b/alerting/internal/evaluator/condition.go index 3aca893..e5c89e0 100644 --- a/alerting/internal/evaluator/condition.go +++ b/alerting/internal/evaluator/condition.go @@ -3,8 +3,8 @@ package evaluator import ( "fmt" - "github.com/sentry/sentry/alerting/internal/queryclient" - "github.com/sentry/sentry/alerting/internal/rulestore" + "github.com/cairnobs/cairnobs/alerting/internal/queryclient" + "github.com/cairnobs/cairnobs/alerting/internal/rulestore" ) // evaluateCondition implements fixes 3 and 4 from diff --git a/alerting/internal/evaluator/condition_test.go b/alerting/internal/evaluator/condition_test.go index 7d69087..52b8c1d 100644 --- a/alerting/internal/evaluator/condition_test.go +++ b/alerting/internal/evaluator/condition_test.go @@ -3,8 +3,8 @@ package evaluator import ( "testing" - "github.com/sentry/sentry/alerting/internal/queryclient" - "github.com/sentry/sentry/alerting/internal/rulestore" + "github.com/cairnobs/cairnobs/alerting/internal/queryclient" + "github.com/cairnobs/cairnobs/alerting/internal/rulestore" ) func thresholdRule(comparator rulestore.Comparator, threshold float64) rulestore.Rule { diff --git a/alerting/internal/evaluator/evaluator.go b/alerting/internal/evaluator/evaluator.go index df1c23a..8f7c5af 100644 --- a/alerting/internal/evaluator/evaluator.go +++ b/alerting/internal/evaluator/evaluator.go @@ -6,10 +6,10 @@ import ( "sync" "time" - "github.com/sentry/sentry/alerting/internal/delivery" - "github.com/sentry/sentry/alerting/internal/notifystore" - "github.com/sentry/sentry/alerting/internal/queryclient" - "github.com/sentry/sentry/alerting/internal/rulestore" + "github.com/cairnobs/cairnobs/alerting/internal/delivery" + "github.com/cairnobs/cairnobs/alerting/internal/notifystore" + "github.com/cairnobs/cairnobs/alerting/internal/queryclient" + "github.com/cairnobs/cairnobs/alerting/internal/rulestore" ) // Evaluator is the ticker-driven scheduler -- a bounded worker pool, not diff --git a/alerting/internal/evaluator/transitions.go b/alerting/internal/evaluator/transitions.go index 93500a6..b382306 100644 --- a/alerting/internal/evaluator/transitions.go +++ b/alerting/internal/evaluator/transitions.go @@ -10,7 +10,7 @@ package evaluator import ( "time" - "github.com/sentry/sentry/alerting/internal/rulestore" + "github.com/cairnobs/cairnobs/alerting/internal/rulestore" ) // TransitionInput is everything ComputeTransition needs: the rule's diff --git a/alerting/internal/evaluator/transitions_test.go b/alerting/internal/evaluator/transitions_test.go index ce6965a..38ba15b 100644 --- a/alerting/internal/evaluator/transitions_test.go +++ b/alerting/internal/evaluator/transitions_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "github.com/sentry/sentry/alerting/internal/rulestore" + "github.com/cairnobs/cairnobs/alerting/internal/rulestore" ) var t0 = time.Date(2026, 8, 14, 0, 0, 0, 0, time.UTC) diff --git a/alerting/internal/httpapi/handler.go b/alerting/internal/httpapi/handler.go index f88b5cd..3180a00 100644 --- a/alerting/internal/httpapi/handler.go +++ b/alerting/internal/httpapi/handler.go @@ -11,8 +11,8 @@ import ( "log/slog" "net/http" - "github.com/sentry/sentry/alerting/internal/notifystore" - "github.com/sentry/sentry/alerting/internal/rulestore" + "github.com/cairnobs/cairnobs/alerting/internal/notifystore" + "github.com/cairnobs/cairnobs/alerting/internal/rulestore" ) type ruleStore interface { diff --git a/alerting/internal/httpapi/handler_test.go b/alerting/internal/httpapi/handler_test.go index d99e7b8..5a24859 100644 --- a/alerting/internal/httpapi/handler_test.go +++ b/alerting/internal/httpapi/handler_test.go @@ -9,8 +9,8 @@ import ( "strings" "testing" - "github.com/sentry/sentry/alerting/internal/notifystore" - "github.com/sentry/sentry/alerting/internal/rulestore" + "github.com/cairnobs/cairnobs/alerting/internal/notifystore" + "github.com/cairnobs/cairnobs/alerting/internal/rulestore" ) type fakeRuleStore struct { diff --git a/alerting/internal/queryclient/client.go b/alerting/internal/queryclient/client.go index f4adc60..7b302e4 100644 --- a/alerting/internal/queryclient/client.go +++ b/alerting/internal/queryclient/client.go @@ -1,6 +1,6 @@ // Package queryclient is a thin HTTP client to /api's POST /query -- // alerting never imports querylang or talks to ClickHouse/Tantivy -// directly, same precedent sentryctl query and the web UI's dashboard +// directly, same precedent cairnobsctl query and the web UI's dashboard // panels already set: one query-execution path, reused everywhere. package queryclient diff --git a/alerting/internal/sessioncheck/middleware.go b/alerting/internal/sessioncheck/middleware.go index 3abc7b3..7ca2d39 100644 --- a/alerting/internal/sessioncheck/middleware.go +++ b/alerting/internal/sessioncheck/middleware.go @@ -10,7 +10,7 @@ import ( // imported -- see this package's doc comment) -- the same cookie // api/localauth.Handler.setCookie writes, scoped (via SESSION_COOKIE_ // DOMAIN) to cover both api's and alerting's subdomains in production. -const sessionCookieName = "sentry_local_session" +const sessionCookieName = "cairnobs_local_session" type errorResponse struct { Error string `json:"error"` diff --git a/alerting/internal/sessioncheck/sessioncheck_integration_test.go b/alerting/internal/sessioncheck/sessioncheck_integration_test.go index 575cc2c..9f04523 100644 --- a/alerting/internal/sessioncheck/sessioncheck_integration_test.go +++ b/alerting/internal/sessioncheck/sessioncheck_integration_test.go @@ -9,7 +9,7 @@ // // docker run --rm --network sentry_default -v $(pwd)/../../..:/src -w /src/alerting \ // -e SESSIONCHECK_TEST_POSTGRES_ADDR=metadata-postgres:5432 \ -// -e SESSIONCHECK_TEST_POSTGRES_PASSWORD=sentry-dev-only \ +// -e SESSIONCHECK_TEST_POSTGRES_PASSWORD=cairnobs-dev-only \ // golang:1.25-alpine go test ./internal/sessioncheck/... -run Integration -v package sessioncheck diff --git a/api/README.md b/api/README.md index 3980cf1..5c5b2b4 100644 --- a/api/README.md +++ b/api/README.md @@ -1,6 +1,6 @@ # api -Sentry's query API: a single `POST /query` endpoint accepting either the +Cairn OBS's query API: a single `POST /query` endpoint accepting either the pipe syntax or raw SQL, compiled and routed across ClickHouse and Tantivy by `internal/querylang`. Replaces Phase 0/1's two separate placeholder endpoints (raw-SQL-only `/query`, free-text-only `/search`) — see diff --git a/api/agents/handler.go b/api/agents/handler.go index 615d5d1..d0d4945 100644 --- a/api/agents/handler.go +++ b/api/agents/handler.go @@ -10,7 +10,7 @@ import ( "path" "strings" - "github.com/sentry/sentry/api/authz" + "github.com/cairnobs/cairnobs/api/authz" ) // store is the narrow interface Handler depends on -- *Store (store.go) diff --git a/api/agents/handler_test.go b/api/agents/handler_test.go index b25c966..22c6a35 100644 --- a/api/agents/handler_test.go +++ b/api/agents/handler_test.go @@ -11,7 +11,7 @@ import ( "net/http/httptest" "testing" - "github.com/sentry/sentry/api/authz" + "github.com/cairnobs/cairnobs/api/authz" ) func discardLogger() *slog.Logger { @@ -259,7 +259,7 @@ func TestHandleSetConfigDenylistsSensitivePaths(t *testing.T) { "/home/alice/.ssh/id_rsa", "/home/alice/.ssh/authorized_keys", "/proc/1/environ", - "/etc/sentry-agent/client-key.pem", + "/etc/cairnobs-agent/client-key.pem", "/opt/app/../../etc/shadow", "/opt/app/id_ed25519", } diff --git a/api/ai/aiapi/handler.go b/api/ai/aiapi/handler.go index 0829125..7e7371d 100644 --- a/api/ai/aiapi/handler.go +++ b/api/ai/aiapi/handler.go @@ -22,12 +22,12 @@ import ( "strings" "time" - "github.com/sentry/sentry/api/ai/costguard" - "github.com/sentry/sentry/api/ai/provider" - "github.com/sentry/sentry/api/ai/router" - "github.com/sentry/sentry/api/authz" - "github.com/sentry/sentry/api/internal/querylang/ir" - "github.com/sentry/sentry/api/internal/querylang/planner" + "github.com/cairnobs/cairnobs/api/ai/costguard" + "github.com/cairnobs/cairnobs/api/ai/provider" + "github.com/cairnobs/cairnobs/api/ai/router" + "github.com/cairnobs/cairnobs/api/authz" + "github.com/cairnobs/cairnobs/api/internal/querylang/ir" + "github.com/cairnobs/cairnobs/api/internal/querylang/planner" ) // SchemaContextSource resolves the calling tenant's grounding data. diff --git a/api/ai/aiapi/handler_test.go b/api/ai/aiapi/handler_test.go index fdb74b2..5917b61 100644 --- a/api/ai/aiapi/handler_test.go +++ b/api/ai/aiapi/handler_test.go @@ -10,8 +10,8 @@ import ( "net/http/httptest" "testing" - "github.com/sentry/sentry/api/ai/provider" - "github.com/sentry/sentry/api/ai/router" + "github.com/cairnobs/cairnobs/api/ai/provider" + "github.com/cairnobs/cairnobs/api/ai/router" ) type fakeProvider struct { diff --git a/api/ai/aiapi/integration_test.go b/api/ai/aiapi/integration_test.go index 2e1b503..0d7d266 100644 --- a/api/ai/aiapi/integration_test.go +++ b/api/ai/aiapi/integration_test.go @@ -29,8 +29,8 @@ import ( "net/http/httptest" "testing" - "github.com/sentry/sentry/api/ai/provider/ollama" - "github.com/sentry/sentry/api/ai/router" + "github.com/cairnobs/cairnobs/api/ai/provider/ollama" + "github.com/cairnobs/cairnobs/api/ai/router" ) // jsonBody marshals v for use as an http.Post body -- the integration diff --git a/api/ai/costguard/costguard.go b/api/ai/costguard/costguard.go index 09a0023..6cdd4cd 100644 --- a/api/ai/costguard/costguard.go +++ b/api/ai/costguard/costguard.go @@ -21,7 +21,7 @@ import ( "strings" "time" - "github.com/sentry/sentry/api/internal/querylang/ir" + "github.com/cairnobs/cairnobs/api/internal/querylang/ir" ) type Level string diff --git a/api/ai/costguard/costguard_test.go b/api/ai/costguard/costguard_test.go index 82603c3..19ca338 100644 --- a/api/ai/costguard/costguard_test.go +++ b/api/ai/costguard/costguard_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "github.com/sentry/sentry/api/internal/querylang/ir" + "github.com/cairnobs/cairnobs/api/internal/querylang/ir" ) // A raw-row (non-aggregation) query with no time range and no explicit diff --git a/api/ai/grounding/grounding.go b/api/ai/grounding/grounding.go index d1f5c7b..6f8bab0 100644 --- a/api/ai/grounding/grounding.go +++ b/api/ai/grounding/grounding.go @@ -25,8 +25,8 @@ import ( "sync" "time" - "github.com/sentry/sentry/api/ai/provider" - "github.com/sentry/sentry/api/querylang/executor" + "github.com/cairnobs/cairnobs/api/ai/provider" + "github.com/cairnobs/cairnobs/api/querylang/executor" ) // staticFields are always present regardless of what's actually been diff --git a/api/ai/grounding/grounding_test.go b/api/ai/grounding/grounding_test.go index 7d5b790..5ceb9f0 100644 --- a/api/ai/grounding/grounding_test.go +++ b/api/ai/grounding/grounding_test.go @@ -6,8 +6,8 @@ import ( "testing" "time" - "github.com/sentry/sentry/api/ai/provider" - "github.com/sentry/sentry/api/querylang/executor" + "github.com/cairnobs/cairnobs/api/ai/provider" + "github.com/cairnobs/cairnobs/api/querylang/executor" ) // routingFakeRunner returns a canned result keyed by a substring match diff --git a/api/ai/provider/ollama/ollama.go b/api/ai/provider/ollama/ollama.go index 3ec1b48..e9de6ef 100644 --- a/api/ai/provider/ollama/ollama.go +++ b/api/ai/provider/ollama/ollama.go @@ -15,7 +15,7 @@ import ( "net/http" "strings" - "github.com/sentry/sentry/api/ai/provider" + "github.com/cairnobs/cairnobs/api/ai/provider" ) // Client implements provider.Provider against one Ollama server and one diff --git a/api/ai/provider/ollama/ollama_test.go b/api/ai/provider/ollama/ollama_test.go index 137fa09..59b7fb1 100644 --- a/api/ai/provider/ollama/ollama_test.go +++ b/api/ai/provider/ollama/ollama_test.go @@ -8,7 +8,7 @@ import ( "strings" "testing" - "github.com/sentry/sentry/api/ai/provider" + "github.com/cairnobs/cairnobs/api/ai/provider" ) // fakeOllamaServer stands in for a real Ollama server, returning the diff --git a/api/ai/provider/ollama/prompts.go b/api/ai/provider/ollama/prompts.go index 674fa07..249d8a5 100644 --- a/api/ai/provider/ollama/prompts.go +++ b/api/ai/provider/ollama/prompts.go @@ -4,18 +4,18 @@ import ( "fmt" "strings" - "github.com/sentry/sentry/api/ai/provider" + "github.com/cairnobs/cairnobs/api/ai/provider" ) // grammarReference is a condensed version of // /docs/query-language-reference.md -- every operation's system prompt -// includes this so the model is grounded in Sentry's actual pipe syntax, +// includes this so the model is grounded in Cairn OBS's actual pipe syntax, // not whatever generic log-query DSL it may have seen in training. // Trimmed to the parts that matter for generation/explanation (the full // doc's prose and examples aren't needed here); kept in sync with that // doc by hand -- if the grammar changes, this needs updating too, same // as any other place the language is described outside its own parser. -const grammarReference = `Sentry query language (pipe syntax): +const grammarReference = `Cairn OBS query language (pipe syntax): | | | ... @@ -64,7 +64,7 @@ func renderSchema(s provider.SchemaContext) string { } func translateSystemPrompt(schema provider.SchemaContext) string { - return fmt.Sprintf(`You translate a plain-English question into a Sentry pipe-syntax query. You never explain, never execute anything, never write raw SQL unless the pipe syntax genuinely cannot express the request. + return fmt.Sprintf(`You translate a plain-English question into a Cairn OBS pipe-syntax query. You never explain, never execute anything, never write raw SQL unless the pipe syntax genuinely cannot express the request. %s @@ -77,7 +77,7 @@ If you cannot produce a query you're reasonably confident in, set confidence to } func completeSystemPrompt(schema provider.SchemaContext) string { - return fmt.Sprintf(`You suggest how to continue a partially-typed Sentry query. You are given everything typed so far; respond with ONLY the suggested continuation text (what should appear after the cursor), not the text already typed, not an explanation. + return fmt.Sprintf(`You suggest how to continue a partially-typed Cairn OBS query. You are given everything typed so far; respond with ONLY the suggested continuation text (what should appear after the cursor), not the text already typed, not an explanation. %s @@ -94,12 +94,12 @@ Respond with ONLY a JSON object, no other text, no markdown fences: // ExplainRequest.RuleFindings' doc comment. func explainSystemPrompt(hasIntent, hasFindings bool) string { if hasFindings { - return fmt.Sprintf(`A rule-based check already found one or more real issues with a Sentry query's efficiency (e.g. a missing time range). Your only job is to phrase those findings as a short, clear, actionable suggestion for the person who wrote the query -- do not invent additional issues, do not restate the query's own syntax back at them, do not hedge with "might" or "could" about something the check already confirmed. One or two sentences. + return fmt.Sprintf(`A rule-based check already found one or more real issues with a Cairn OBS query's efficiency (e.g. a missing time range). Your only job is to phrase those findings as a short, clear, actionable suggestion for the person who wrote the query -- do not invent additional issues, do not restate the query's own syntax back at them, do not hedge with "might" or "could" about something the check already confirmed. One or two sentences. %s`, grammarReference) } - base := fmt.Sprintf(`You explain what a Sentry query does in plain English, for someone who may not know the query language. Be concise -- two or three sentences, not a line-by-line breakdown unless the query is unusually complex. + base := fmt.Sprintf(`You explain what a Cairn OBS query does in plain English, for someone who may not know the query language. Be concise -- two or three sentences, not a line-by-line breakdown unless the query is unusually complex. %s`, grammarReference) if hasIntent { @@ -109,7 +109,7 @@ func explainSystemPrompt(hasIntent, hasFindings bool) string { } func fixSystemPrompt(schema provider.SchemaContext) string { - return fmt.Sprintf(`You fix a broken Sentry query given its error message. Produce a corrected query and a short explanation of what was wrong. + return fmt.Sprintf(`You fix a broken Cairn OBS query given its error message. Produce a corrected query and a short explanation of what was wrong. %s diff --git a/api/ai/router/router.go b/api/ai/router/router.go index 63e0126..d2ff25e 100644 --- a/api/ai/router/router.go +++ b/api/ai/router/router.go @@ -14,7 +14,7 @@ // consume it). package router -import "github.com/sentry/sentry/api/ai/provider" +import "github.com/cairnobs/cairnobs/api/ai/provider" type Operation string diff --git a/api/ai/router/router_test.go b/api/ai/router/router_test.go index 24e5062..e63c468 100644 --- a/api/ai/router/router_test.go +++ b/api/ai/router/router_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/sentry/sentry/api/ai/provider" + "github.com/cairnobs/cairnobs/api/ai/provider" ) // namedFakeProvider lets a test tell which configured provider actually diff --git a/api/authz/httpauthz_test.go b/api/authz/httpauthz_test.go index dd4c8a5..5ab9dd8 100644 --- a/api/authz/httpauthz_test.go +++ b/api/authz/httpauthz_test.go @@ -19,7 +19,7 @@ func TestHTTPAuthorizerForwardsCredentialsAndParsesIdentity(t *testing.T) { a := NewHTTPAuthorizer(srv.URL) incoming := httptest.NewRequest(http.MethodPost, "/query", nil) - incoming.Header.Set("Cookie", "sentry_session=abc123") + incoming.Header.Set("Cookie", "cairnobs_session=abc123") incoming.Header.Set("Authorization", "Bearer service-token-xyz") identity, err := a.Authorize(incoming) @@ -29,7 +29,7 @@ func TestHTTPAuthorizerForwardsCredentialsAndParsesIdentity(t *testing.T) { if identity.TenantID != "acme" || identity.UserID != "u1" || identity.Role != RoleEditor { t.Fatalf("unexpected identity: %+v", identity) } - if gotCookie != "sentry_session=abc123" { + if gotCookie != "cairnobs_session=abc123" { t.Fatalf("Cookie header not forwarded, got %q", gotCookie) } if gotAuth != "Bearer service-token-xyz" { diff --git a/api/cmd/api/main.go b/api/cmd/api/main.go index 295376b..54fdf89 100644 --- a/api/cmd/api/main.go +++ b/api/cmd/api/main.go @@ -1,4 +1,4 @@ -// Command api is Sentry's query API: a single POST /query endpoint +// Command api is Cairn OBS's query API: a single POST /query endpoint // accepting either the pipe syntax or raw SQL, compiled and routed // across ClickHouse and search by internal/querylang. See // queryapi and /docs/query-language-design.md for why this is @@ -23,20 +23,20 @@ import ( "github.com/ClickHouse/clickhouse-go/v2" "github.com/jackc/pgx/v5/pgxpool" - "github.com/sentry/sentry/api/agents" - "github.com/sentry/sentry/api/ai/aiapi" - "github.com/sentry/sentry/api/ai/grounding" - "github.com/sentry/sentry/api/ai/provider/ollama" - "github.com/sentry/sentry/api/ai/router" - "github.com/sentry/sentry/api/authz" - "github.com/sentry/sentry/api/dashboards" - "github.com/sentry/sentry/api/httpserver" - "github.com/sentry/sentry/api/internal/config" - "github.com/sentry/sentry/api/localauth" - "github.com/sentry/sentry/api/logretention" - "github.com/sentry/sentry/api/queryapi" - "github.com/sentry/sentry/api/querylang/executor" - "github.com/sentry/sentry/api/searchclient" + "github.com/cairnobs/cairnobs/api/agents" + "github.com/cairnobs/cairnobs/api/ai/aiapi" + "github.com/cairnobs/cairnobs/api/ai/grounding" + "github.com/cairnobs/cairnobs/api/ai/provider/ollama" + "github.com/cairnobs/cairnobs/api/ai/router" + "github.com/cairnobs/cairnobs/api/authz" + "github.com/cairnobs/cairnobs/api/dashboards" + "github.com/cairnobs/cairnobs/api/httpserver" + "github.com/cairnobs/cairnobs/api/internal/config" + "github.com/cairnobs/cairnobs/api/localauth" + "github.com/cairnobs/cairnobs/api/logretention" + "github.com/cairnobs/cairnobs/api/queryapi" + "github.com/cairnobs/cairnobs/api/querylang/executor" + "github.com/cairnobs/cairnobs/api/searchclient" ) // groundingRefreshInterval matches chwriter.Registry/search's diff --git a/api/dashboards/handler.go b/api/dashboards/handler.go index 42fe2c5..b16b499 100644 --- a/api/dashboards/handler.go +++ b/api/dashboards/handler.go @@ -7,7 +7,7 @@ import ( "log/slog" "net/http" - "github.com/sentry/sentry/api/authz" + "github.com/cairnobs/cairnobs/api/authz" ) // store is the narrow interface Handler depends on -- *Store (store.go) @@ -187,7 +187,7 @@ func (h *Handler) handleGet(w http.ResponseWriter, r *http.Request) { func (h *Handler) handleExport(w http.ResponseWriter, r *http.Request) { // Export is the same document GET /dashboards/{id} returns -- the // import endpoint below consumes exactly this shape, and so does - // `sentryctl dashboards apply`, so there's one JSON contract used + // `cairnobsctl dashboards apply`, so there's one JSON contract used // from every call site rather than a bespoke export format. h.handleGet(w, r) } diff --git a/api/dashboards/handler_test.go b/api/dashboards/handler_test.go index 3f64d0c..c3744d6 100644 --- a/api/dashboards/handler_test.go +++ b/api/dashboards/handler_test.go @@ -11,7 +11,7 @@ import ( "strings" "testing" - "github.com/sentry/sentry/api/authz" + "github.com/cairnobs/cairnobs/api/authz" ) // fakeStore enforces tenant scoping the same way store.go's real diff --git a/api/dashboards/permissions.go b/api/dashboards/permissions.go index 8061427..4e0640f 100644 --- a/api/dashboards/permissions.go +++ b/api/dashboards/permissions.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/sentry/sentry/api/authz" + "github.com/cairnobs/cairnobs/api/authz" ) // Permission is one dashboard_permissions row -- see diff --git a/api/dashboards/store_integration_test.go b/api/dashboards/store_integration_test.go index eeb7269..89a2534 100644 --- a/api/dashboards/store_integration_test.go +++ b/api/dashboards/store_integration_test.go @@ -11,7 +11,7 @@ // // docker run --rm --network sentry_default -v $(pwd)/../../..:/src -w /src/api \ // -e DASHBOARDS_TEST_POSTGRES_ADDR=metadata-postgres:5432 \ -// -e DASHBOARDS_TEST_POSTGRES_PASSWORD=sentry-dev-only \ +// -e DASHBOARDS_TEST_POSTGRES_PASSWORD=cairnobs-dev-only \ // golang:1.25-alpine go test ./dashboards/... -run Integration -v package dashboards diff --git a/api/go.mod b/api/go.mod index 570c632..6543f7f 100644 --- a/api/go.mod +++ b/api/go.mod @@ -1,4 +1,4 @@ -module github.com/sentry/sentry/api +module github.com/cairnobs/cairnobs/api go 1.25.0 @@ -6,12 +6,12 @@ require ( github.com/ClickHouse/clickhouse-go/v2 v2.48.0 github.com/google/uuid v1.6.0 github.com/jackc/pgx/v5 v5.10.0 - github.com/sentry/sentry/proto v0.0.0-00010101000000-000000000000 + github.com/cairnobs/cairnobs/proto v0.0.0-00010101000000-000000000000 golang.org/x/crypto v0.55.0 google.golang.org/grpc v1.83.0 ) -replace github.com/sentry/sentry/proto => ../proto +replace github.com/cairnobs/cairnobs/proto => ../proto require ( github.com/ClickHouse/ch-go v0.74.0 // indirect diff --git a/api/internal/config/config.go b/api/internal/config/config.go index e1176b8..fa80084 100644 --- a/api/internal/config/config.go +++ b/api/internal/config/config.go @@ -82,7 +82,7 @@ type PostgresConfig struct { // real deployment that skips docker-compose.override.yml would // otherwise go live with a password anyone can read straight off // GitHub. See DevCredentialWarnings. -const devOnlyCredential = "sentry-dev-only" +const devOnlyCredential = "cairnobs-dev-only" // DevCredentialWarnings reports which configured credentials still // equal docker-compose.yml's literal dev-only default -- cmd/api/main.go diff --git a/api/internal/config/config_test.go b/api/internal/config/config_test.go index 52cb4ce..0847af9 100644 --- a/api/internal/config/config_test.go +++ b/api/internal/config/config_test.go @@ -33,7 +33,7 @@ func TestLoadInvalidTimeoutErrors(t *testing.T) { // TestDevCredentialWarnings is the regression test for the // security-audit finding that docker-compose.yml's hardcoded -// "sentry-dev-only" password has no runtime fail-safe if an operator +// "cairnobs-dev-only" password has no runtime fail-safe if an operator // forgets to override it for a real deployment. func TestDevCredentialWarnings(t *testing.T) { if got := (Config{}).DevCredentialWarnings(); len(got) != 0 { diff --git a/api/internal/querylang/parser/parser.go b/api/internal/querylang/parser/parser.go index 16d9812..01201e7 100644 --- a/api/internal/querylang/parser/parser.go +++ b/api/internal/querylang/parser/parser.go @@ -9,8 +9,8 @@ import ( "fmt" "strconv" - "github.com/sentry/sentry/api/internal/querylang/ast" - "github.com/sentry/sentry/api/internal/querylang/lexer" + "github.com/cairnobs/cairnobs/api/internal/querylang/ast" + "github.com/cairnobs/cairnobs/api/internal/querylang/lexer" ) // Parse parses a pipe-syntax query. Callers are responsible for routing diff --git a/api/internal/querylang/parser/parser_test.go b/api/internal/querylang/parser/parser_test.go index 4f8dc3b..4f78ddd 100644 --- a/api/internal/querylang/parser/parser_test.go +++ b/api/internal/querylang/parser/parser_test.go @@ -3,7 +3,7 @@ package parser import ( "testing" - "github.com/sentry/sentry/api/internal/querylang/ast" + "github.com/cairnobs/cairnobs/api/internal/querylang/ast" ) func TestParseSimpleFilter(t *testing.T) { diff --git a/api/internal/querylang/planner/planner.go b/api/internal/querylang/planner/planner.go index 80257fc..edc5cca 100644 --- a/api/internal/querylang/planner/planner.go +++ b/api/internal/querylang/planner/planner.go @@ -9,9 +9,9 @@ import ( "strings" "time" - "github.com/sentry/sentry/api/internal/querylang/ast" - "github.com/sentry/sentry/api/internal/querylang/ir" - "github.com/sentry/sentry/api/internal/querylang/parser" + "github.com/cairnobs/cairnobs/api/internal/querylang/ast" + "github.com/cairnobs/cairnobs/api/internal/querylang/ir" + "github.com/cairnobs/cairnobs/api/internal/querylang/parser" ) // Language selects which syntax a query is written in. diff --git a/api/internal/querylang/planner/planner_test.go b/api/internal/querylang/planner/planner_test.go index 3588c68..3272bda 100644 --- a/api/internal/querylang/planner/planner_test.go +++ b/api/internal/querylang/planner/planner_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/sentry/sentry/api/internal/querylang/ir" + "github.com/cairnobs/cairnobs/api/internal/querylang/ir" ) var fixedNow = time.Date(2026, 8, 14, 12, 0, 0, 0, time.UTC) diff --git a/api/localauth/authorizer.go b/api/localauth/authorizer.go index e601a02..2062b84 100644 --- a/api/localauth/authorizer.go +++ b/api/localauth/authorizer.go @@ -5,7 +5,7 @@ import ( "errors" "net/http" - "github.com/sentry/sentry/api/authz" + "github.com/cairnobs/cairnobs/api/authz" ) // sessionStore is the narrow interface Authorizer depends on -- *Store @@ -19,7 +19,7 @@ type sessionStore interface { // needs to know about implicitly (via credentials: 'include', not by // name -- the browser handles the cookie, JS never reads it since it's // HttpOnly). -const sessionCookieName = "sentry_local_session" +const sessionCookieName = "cairnobs_local_session" // Authorizer implements api/authz.Authorizer against local_sessions -- // wiring a non-nil *Authorizer into api/cmd/api/main.go's authorizer @@ -37,7 +37,7 @@ func NewAuthorizer(store sessionStore) *Authorizer { var errNoCredential = errors.New("localauth: no session credential presented") -// Authorize checks Authorization: Bearer first (sentryctl and other +// Authorize checks Authorization: Bearer first (cairnobsctl and other // non-browser callers), then the session cookie (the web UI) -- same // precedence authz.HTTPAuthorizer's caller-side forwarding implies, // and the same reason POST /auth/login's response body returns the raw diff --git a/api/localauth/fake_test.go b/api/localauth/fake_test.go index 3ad4cc6..09bbdeb 100644 --- a/api/localauth/fake_test.go +++ b/api/localauth/fake_test.go @@ -5,7 +5,7 @@ import ( "strconv" "time" - "github.com/sentry/sentry/api/authz" + "github.com/cairnobs/cairnobs/api/authz" ) // fakeStore implements both store (handler.go) and sessionStore diff --git a/api/localauth/handler.go b/api/localauth/handler.go index f89874a..17d37ba 100644 --- a/api/localauth/handler.go +++ b/api/localauth/handler.go @@ -8,7 +8,7 @@ import ( "net/http" "time" - "github.com/sentry/sentry/api/authz" + "github.com/cairnobs/cairnobs/api/authz" ) const maxBodyBytes = 1 << 20 // 1 MiB, same cap as queryapi/dashboards/agents @@ -36,8 +36,8 @@ type store interface { type CookieConfig struct { // Domain is typically empty for local dev (host-only cookie, works // fine when web/api are both localhost:) and something like - // ".sentry.example.com" in production, so the same cookie is sent to - // api.sentry.example.com and alerting.sentry.example.com too -- see + // ".cairnobs.example.com" in production, so the same cookie is sent to + // api.cairnobs.example.com and alerting.cairnobs.example.com too -- see // /docs (deployment runbook) for the subdomain scheme this assumes. Domain string // Secure defaults to true (the cookie is never sent over plain @@ -129,7 +129,7 @@ type loginRequest struct { type sessionResponse struct { // Token duplicates what the Set-Cookie header already carries, // specifically for non-browser callers with no cookie jar -- - // sentryctl captures this into SENTRYCTL_TOKEN and sends it back as + // cairnobsctl captures this into CAIRNOBSCTL_TOKEN and sends it back as // Authorization: Bearer (see authorizer.go's credentialFromRequest, // which accepts either). The web UI ignores this field entirely and // relies on the cookie. diff --git a/api/localauth/handler_test.go b/api/localauth/handler_test.go index b16e347..76bb0e6 100644 --- a/api/localauth/handler_test.go +++ b/api/localauth/handler_test.go @@ -10,7 +10,7 @@ import ( "testing" "time" - "github.com/sentry/sentry/api/authz" + "github.com/cairnobs/cairnobs/api/authz" ) func newTestHandler(t *testing.T, fs *fakeStore) (*Handler, *http.ServeMux) { diff --git a/api/localauth/ratelimit_test.go b/api/localauth/ratelimit_test.go index 7ae4bdf..ac55cde 100644 --- a/api/localauth/ratelimit_test.go +++ b/api/localauth/ratelimit_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/sentry/sentry/api/authz" + "github.com/cairnobs/cairnobs/api/authz" ) func TestLoginLimiterAllowsUpToMax(t *testing.T) { diff --git a/api/localauth/store.go b/api/localauth/store.go index 64248ec..2de693c 100644 --- a/api/localauth/store.go +++ b/api/localauth/store.go @@ -37,7 +37,7 @@ import ( "github.com/jackc/pgx/v5/pgconn" "github.com/jackc/pgx/v5/pgxpool" - "github.com/sentry/sentry/api/authz" + "github.com/cairnobs/cairnobs/api/authz" ) var ( diff --git a/api/localauth/store_integration_test.go b/api/localauth/store_integration_test.go index 08a0635..5b11ec0 100644 --- a/api/localauth/store_integration_test.go +++ b/api/localauth/store_integration_test.go @@ -10,7 +10,7 @@ // // docker run --rm --network sentry_default -v $(pwd)/../../..:/src -w /src/api \ // -e LOCALAUTH_TEST_POSTGRES_ADDR=metadata-postgres:5432 \ -// -e LOCALAUTH_TEST_POSTGRES_PASSWORD=sentry-dev-only \ +// -e LOCALAUTH_TEST_POSTGRES_PASSWORD=cairnobs-dev-only \ // golang:1.25-alpine go test ./localauth/... -run Integration -v package localauth @@ -25,7 +25,7 @@ import ( "github.com/google/uuid" "github.com/jackc/pgx/v5/pgxpool" - "github.com/sentry/sentry/api/authz" + "github.com/cairnobs/cairnobs/api/authz" ) func integrationStore(t *testing.T) *Store { diff --git a/api/logretention/handler.go b/api/logretention/handler.go index 9148c03..a34e26a 100644 --- a/api/logretention/handler.go +++ b/api/logretention/handler.go @@ -8,7 +8,7 @@ import ( "strconv" "time" - "github.com/sentry/sentry/api/authz" + "github.com/cairnobs/cairnobs/api/authz" ) const maxBodyBytes = 1 << 20 // 1 MiB, same cap as localauth/dashboards/agents diff --git a/api/logretention/handler_test.go b/api/logretention/handler_test.go index d6e6e5f..f6ec2fe 100644 --- a/api/logretention/handler_test.go +++ b/api/logretention/handler_test.go @@ -13,7 +13,7 @@ import ( "testing" "time" - "github.com/sentry/sentry/api/authz" + "github.com/cairnobs/cairnobs/api/authz" ) func discardLogger() *slog.Logger { diff --git a/api/logretention/store.go b/api/logretention/store.go index 6d2872f..a9cb02b 100644 --- a/api/logretention/store.go +++ b/api/logretention/store.go @@ -13,7 +13,7 @@ // service is a genuine per-log-record dimension already, not something // this package invents: storage/migrations/0001_create_logs_table.sql // has always had a `service` column, and distinct services on one host -// are a real, already-supported shape (separate sentry-agent processes +// are a real, already-supported shape (separate cairnobs-agent processes // on the same machine, each with its own agent.toml `service` -- see // /docs/agent-management-design.md), not merely a per-agent label. // diff --git a/api/queryapi/handler.go b/api/queryapi/handler.go index 01b0c73..45770fe 100644 --- a/api/queryapi/handler.go +++ b/api/queryapi/handler.go @@ -1,4 +1,4 @@ -// Package queryapi is Sentry's query API: a single POST /query endpoint +// Package queryapi is Cairn OBS's query API: a single POST /query endpoint // accepting either the pipe syntax or raw SQL, compiled by // querylang/planner and executed by querylang/executor. Replaces Phase // 0/1's two separate placeholder endpoints (raw-SQL-only /query, @@ -19,10 +19,10 @@ import ( "strings" "time" - "github.com/sentry/sentry/api/ai/costguard" - "github.com/sentry/sentry/api/authz" - "github.com/sentry/sentry/api/internal/querylang/planner" - "github.com/sentry/sentry/api/querylang/executor" + "github.com/cairnobs/cairnobs/api/ai/costguard" + "github.com/cairnobs/cairnobs/api/authz" + "github.com/cairnobs/cairnobs/api/internal/querylang/planner" + "github.com/cairnobs/cairnobs/api/querylang/executor" ) // AuditLogger is core's extension point for query audit logging -- diff --git a/api/queryapi/handler_test.go b/api/queryapi/handler_test.go index 6d63f64..83d782f 100644 --- a/api/queryapi/handler_test.go +++ b/api/queryapi/handler_test.go @@ -12,8 +12,8 @@ import ( "testing" "time" - "github.com/sentry/sentry/api/authz" - "github.com/sentry/sentry/api/querylang/executor" + "github.com/cairnobs/cairnobs/api/authz" + "github.com/cairnobs/cairnobs/api/querylang/executor" ) type fakeSQLRunner struct { diff --git a/api/querylang/executor/executor.go b/api/querylang/executor/executor.go index 7f36c78..598af3e 100644 --- a/api/querylang/executor/executor.go +++ b/api/querylang/executor/executor.go @@ -9,7 +9,7 @@ import ( "context" "fmt" - "github.com/sentry/sentry/api/internal/querylang/ir" + "github.com/cairnobs/cairnobs/api/internal/querylang/ir" ) type Result struct { diff --git a/api/querylang/executor/executor_test.go b/api/querylang/executor/executor_test.go index 452ed7a..fdb1bd6 100644 --- a/api/querylang/executor/executor_test.go +++ b/api/querylang/executor/executor_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/sentry/sentry/api/internal/querylang/ir" + "github.com/cairnobs/cairnobs/api/internal/querylang/ir" ) func mustParseTime(t *testing.T, s string) time.Time { diff --git a/api/querylang/executor/sql.go b/api/querylang/executor/sql.go index 4363818..eb5d31b 100644 --- a/api/querylang/executor/sql.go +++ b/api/querylang/executor/sql.go @@ -6,7 +6,7 @@ import ( "strings" "time" - "github.com/sentry/sentry/api/internal/querylang/ir" + "github.com/cairnobs/cairnobs/api/internal/querylang/ir" ) // defaultRowLimit is the safety net when a raw-row query has neither an diff --git a/api/searchclient/client.go b/api/searchclient/client.go index 0b1d6ad..93bedf3 100644 --- a/api/searchclient/client.go +++ b/api/searchclient/client.go @@ -12,7 +12,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - searchv1 "github.com/sentry/sentry/proto/sentry/search/v1" + searchv1 "github.com/cairnobs/cairnobs/proto/sentry/search/v1" ) type Client struct { diff --git a/cli/Dockerfile b/cli/Dockerfile index 5f71417..ea3c749 100644 --- a/cli/Dockerfile +++ b/cli/Dockerfile @@ -1,9 +1,9 @@ -# docker build -f cli/Dockerfile -t sentryctl cli/ +# docker build -f cli/Dockerfile -t cairnobsctl cli/ FROM golang:1.25-alpine AS builder WORKDIR /src COPY . . -RUN CGO_ENABLED=0 GOOS=linux go build -o /out/sentryctl ./cmd/sentryctl +RUN CGO_ENABLED=0 GOOS=linux go build -o /out/cairnobsctl ./cmd/cairnobsctl FROM gcr.io/distroless/static-debian12 -COPY --from=builder /out/sentryctl /sentryctl -ENTRYPOINT ["/sentryctl"] +COPY --from=builder /out/cairnobsctl /cairnobsctl +ENTRYPOINT ["/cairnobsctl"] diff --git a/cli/README.md b/cli/README.md index 0619daf..b3b1ba1 100644 --- a/cli/README.md +++ b/cli/README.md @@ -1,20 +1,20 @@ -# sentryctl +# cairnobsctl -Sentry's control CLI. +Cairn OBS's control CLI. ```sh -sentryctl ping # checks http://localhost:8080/healthz -sentryctl ping --api http://api.internal:8080 -SENTRYCTL_API_URL=http://api.internal:8080 sentryctl ping +cairnobsctl ping # checks http://localhost:8080/healthz +cairnobsctl ping --api http://api.internal:8080 +CAIRNOBSCTL_API_URL=http://api.internal:8080 cairnobsctl ping ``` Exits 0 and prints `ok` if `/api`'s `/healthz` responds 200; exits 1 with an error on `stderr` otherwise. ```sh -sentryctl query 'service=api | where status>=500 | stats count by host' -sentryctl query 'SELECT * FROM logs LIMIT 10' --language sql -sentryctl query 'message:"connection refused"' --json +cairnobsctl query 'service=api | where status>=500 | stats count by host' +cairnobsctl query 'SELECT * FROM logs LIMIT 10' --language sql +cairnobsctl query 'message:"connection refused"' --json ``` Quote the query in your shell — pipe syntax uses `|`, which your shell @@ -27,17 +27,17 @@ Prints a table by default (stdlib `text/tabwriter`, no new dependency); `--json` prints the raw `{columns, rows}` response instead. ```sh -sentryctl dashboards list -sentryctl dashboards get -sentryctl dashboards apply dashboard.json # imports a dashboard exported via the web UI's "Export JSON" button +cairnobsctl dashboards list +cairnobsctl dashboards get +cairnobsctl dashboards apply dashboard.json # imports a dashboard exported via the web UI's "Export JSON" button -sentryctl dashboards permissions list -sentryctl dashboards permissions grant viewer|editor -sentryctl dashboards permissions revoke +cairnobsctl dashboards permissions list +cairnobsctl dashboards permissions grant viewer|editor +cairnobsctl dashboards permissions revoke -sentryctl alerts list -sentryctl alerts get -sentryctl alerts apply rule.json # creates a rule from a JSON file shaped like POST /rules's body +cairnobsctl alerts list +cairnobsctl alerts get +cairnobsctl alerts apply rule.json # creates a rule from a JSON file shaped like POST /rules's body ``` `dashboards permissions` is Phase 4's per-resource dashboard grant @@ -51,7 +51,7 @@ CLI bug. `dashboards` talks to `/api` (`--api`, same override as `query`/`ping`). `alerts` talks to `/alerting`, a separate service with its own base URL -(`--alerting-api`, or `$SENTRYCTL_ALERTING_API_URL`, default +(`--alerting-api`, or `$CAIRNOBSCTL_ALERTING_API_URL`, default `http://localhost:8081`) — see `/docs/phase-3-alerting-design.md`'s component boundary for why alerting isn't just another `/api` route. `apply` in both cases sends the file's JSON as-is to the corresponding @@ -78,5 +78,5 @@ go test ./... ``` ```sh -docker build -f Dockerfile -t sentryctl . # context is cli/, not the repo root +docker build -f Dockerfile -t cairnobsctl . # context is cli/, not the repo root ``` diff --git a/cli/cmd/sentryctl/auth_test.go b/cli/cmd/cairnobsctl/auth_test.go similarity index 96% rename from cli/cmd/sentryctl/auth_test.go rename to cli/cmd/cairnobsctl/auth_test.go index 847d325..ce13173 100644 --- a/cli/cmd/sentryctl/auth_test.go +++ b/cli/cmd/cairnobsctl/auth_test.go @@ -10,7 +10,7 @@ import ( func TestResolveTokenFromEnv(t *testing.T) { env := func(k string) string { - if k == "SENTRYCTL_TOKEN" { + if k == "CAIRNOBSCTL_TOKEN" { return "secret-token" } return "" @@ -91,7 +91,7 @@ func TestCmdPingForwardsBearerToken(t *testing.T) { })) defer srv.Close() - t.Setenv("SENTRYCTL_TOKEN", "ping-token") + t.Setenv("CAIRNOBSCTL_TOKEN", "ping-token") var stdout, stderr bytes.Buffer code := cmdPing([]string{"--api", srv.URL}, &stdout, &stderr) if code != 0 { @@ -111,7 +111,7 @@ func TestCmdQueryForwardsBearerToken(t *testing.T) { })) defer srv.Close() - t.Setenv("SENTRYCTL_TOKEN", "query-token") + t.Setenv("CAIRNOBSCTL_TOKEN", "query-token") var stdout, stderr bytes.Buffer code := cmdQuery([]string{"--api", srv.URL, "service=api"}, &stdout, &stderr) if code != 0 { diff --git a/cli/cmd/sentryctl/cmd_agents.go b/cli/cmd/cairnobsctl/cmd_agents.go similarity index 82% rename from cli/cmd/sentryctl/cmd_agents.go rename to cli/cmd/cairnobsctl/cmd_agents.go index 86219de..7294796 100644 --- a/cli/cmd/sentryctl/cmd_agents.go +++ b/cli/cmd/cairnobsctl/cmd_agents.go @@ -17,7 +17,7 @@ import ( func cmdAgents(args []string, stdout, stderr io.Writer) int { if len(args) == 0 { - fmt.Fprintln(stderr, "sentryctl agents: expected a subcommand (list, get, config, restart)") + fmt.Fprintln(stderr, "cairnobsctl agents: expected a subcommand (list, get, config, restart)") return 1 } apiURL, rest := extractAPIFlag(args[1:], os.Getenv) @@ -28,19 +28,19 @@ func cmdAgents(args []string, stdout, stderr io.Writer) int { return httpGetJSON(apiURL, "/agents", token, stdout, stderr) case "get": if len(rest) == 0 { - fmt.Fprintln(stderr, "sentryctl agents get: missing host") + fmt.Fprintln(stderr, "cairnobsctl agents get: missing host") return 1 } return httpGetJSON(apiURL, "/agents/"+rest[0], token, stdout, stderr) case "config": if len(rest) == 0 { - fmt.Fprintln(stderr, "sentryctl agents config: expected a subcommand (get, set, clear)") + fmt.Fprintln(stderr, "cairnobsctl agents config: expected a subcommand (get, set, clear)") return 1 } return cmdAgentsConfig(rest, apiURL, token, stdout, stderr) case "restart": if len(rest) == 0 { - fmt.Fprintln(stderr, "sentryctl agents restart: missing host") + fmt.Fprintln(stderr, "cairnobsctl agents restart: missing host") return 1 } // os.Stdin passed explicitly at this inner layer (not threaded @@ -49,7 +49,7 @@ func cmdAgents(args []string, stdout, stderr io.Writer) int { // call cmdAgentsRestart directly with a fake reader. return cmdAgentsRestart(rest, apiURL, token, os.Stdin, stdout, stderr) default: - fmt.Fprintf(stderr, "sentryctl agents: unknown subcommand %q (want list, get, config, restart)\n", args[0]) + fmt.Fprintf(stderr, "cairnobsctl agents: unknown subcommand %q (want list, get, config, restart)\n", args[0]) return 1 } } @@ -59,7 +59,7 @@ func cmdAgentsConfig(args []string, apiURL, token string, stdout, stderr io.Writ switch sub { case "get": if len(rest) == 0 { - fmt.Fprintln(stderr, "sentryctl agents config get: missing host") + fmt.Fprintln(stderr, "cairnobsctl agents config get: missing host") return 1 } // Same GET /agents/{host} as plain "get" -- an agent's reported @@ -69,18 +69,18 @@ func cmdAgentsConfig(args []string, apiURL, token string, stdout, stderr io.Writ return httpGetJSON(apiURL, "/agents/"+rest[0], token, stdout, stderr) case "set": if len(rest) == 0 { - fmt.Fprintln(stderr, "sentryctl agents config set: missing host") + fmt.Fprintln(stderr, "cairnobsctl agents config set: missing host") return 1 } return cmdAgentsConfigSet(rest[0], rest[1:], apiURL, token, stdout, stderr) case "clear": if len(rest) == 0 { - fmt.Fprintln(stderr, "sentryctl agents config clear: missing host") + fmt.Fprintln(stderr, "cairnobsctl agents config clear: missing host") return 1 } return httpMutateNoBody(http.MethodDelete, apiURL, "/agents/"+rest[0]+"/config", token, "", "config override cleared -- agent will run its local agent.toml again", stdout, stderr) default: - fmt.Fprintf(stderr, "sentryctl agents config: unknown subcommand %q (want get, set, clear)\n", sub) + fmt.Fprintf(stderr, "cairnobsctl agents config: unknown subcommand %q (want get, set, clear)\n", sub) return 1 } } @@ -135,71 +135,71 @@ func cmdAgentsConfigSet(host string, flagArgs []string, apiURL, token string, st case "--batch-max-size": v, ok := next() if !ok { - fmt.Fprintln(stderr, "sentryctl agents config set: --batch-max-size requires a value") + fmt.Fprintln(stderr, "cairnobsctl agents config set: --batch-max-size requires a value") return 1 } n, err := strconv.ParseInt(v, 10, 64) if err != nil { - fmt.Fprintf(stderr, "sentryctl agents config set: invalid --batch-max-size %q: %v\n", v, err) + fmt.Fprintf(stderr, "cairnobsctl agents config set: invalid --batch-max-size %q: %v\n", v, err) return 1 } batchMaxSize = &n case "--batch-flush-interval-ms": v, ok := next() if !ok { - fmt.Fprintln(stderr, "sentryctl agents config set: --batch-flush-interval-ms requires a value") + fmt.Fprintln(stderr, "cairnobsctl agents config set: --batch-flush-interval-ms requires a value") return 1 } n, err := strconv.ParseInt(v, 10, 64) if err != nil { - fmt.Fprintf(stderr, "sentryctl agents config set: invalid --batch-flush-interval-ms %q: %v\n", v, err) + fmt.Fprintf(stderr, "cairnobsctl agents config set: invalid --batch-flush-interval-ms %q: %v\n", v, err) return 1 } batchFlushMS = &n case "--heartbeat-interval-ms": v, ok := next() if !ok { - fmt.Fprintln(stderr, "sentryctl agents config set: --heartbeat-interval-ms requires a value") + fmt.Fprintln(stderr, "cairnobsctl agents config set: --heartbeat-interval-ms requires a value") return 1 } n, err := strconv.ParseInt(v, 10, 64) if err != nil { - fmt.Fprintf(stderr, "sentryctl agents config set: invalid --heartbeat-interval-ms %q: %v\n", v, err) + fmt.Fprintf(stderr, "cairnobsctl agents config set: invalid --heartbeat-interval-ms %q: %v\n", v, err) return 1 } heartbeatMS = &n case "--heartbeat-enabled": v, ok := next() if !ok { - fmt.Fprintln(stderr, "sentryctl agents config set: --heartbeat-enabled requires true or false") + fmt.Fprintln(stderr, "cairnobsctl agents config set: --heartbeat-enabled requires true or false") return 1 } b, err := strconv.ParseBool(v) if err != nil { - fmt.Fprintf(stderr, "sentryctl agents config set: invalid --heartbeat-enabled %q: %v\n", v, err) + fmt.Fprintf(stderr, "cairnobsctl agents config set: invalid --heartbeat-enabled %q: %v\n", v, err) return 1 } heartbeatEnabled = &b case "--journald-unit": v, ok := next() if !ok { - fmt.Fprintln(stderr, "sentryctl agents config set: --journald-unit requires a value (empty string clears the filter)") + fmt.Fprintln(stderr, "cairnobsctl agents config set: --journald-unit requires a value (empty string clears the filter)") return 1 } journaldUnit = &v default: - fmt.Fprintf(stderr, "sentryctl agents config set: unknown flag %q\n", flag) + fmt.Fprintf(stderr, "cairnobsctl agents config set: unknown flag %q\n", flag) return 1 } } if batchMaxSize == nil && batchFlushMS == nil && heartbeatMS == nil && heartbeatEnabled == nil && journaldUnit == nil { - fmt.Fprintln(stderr, "sentryctl agents config set: at least one of --batch-max-size, --batch-flush-interval-ms, --heartbeat-enabled, --heartbeat-interval-ms, --journald-unit is required") + fmt.Fprintln(stderr, "cairnobsctl agents config set: at least one of --batch-max-size, --batch-flush-interval-ms, --heartbeat-enabled, --heartbeat-interval-ms, --journald-unit is required") return 1 } current, err := fetchAgent(apiURL, host, token) if err != nil { - fmt.Fprintf(stderr, "sentryctl agents config set: fetching current state: %v\n", err) + fmt.Fprintf(stderr, "cairnobsctl agents config set: fetching current state: %v\n", err) return 1 } @@ -232,7 +232,7 @@ func cmdAgentsConfigSet(host string, flagArgs []string, apiURL, token string, st body, err := json.Marshal(merged) if err != nil { - fmt.Fprintf(stderr, "sentryctl agents config set: encoding request: %v\n", err) + fmt.Fprintf(stderr, "cairnobsctl agents config set: encoding request: %v\n", err) return 1 } return httpPutJSON(apiURL, "/agents/"+host+"/config", token, string(body), stdout, stderr) diff --git a/cli/cmd/sentryctl/cmd_agents_test.go b/cli/cmd/cairnobsctl/cmd_agents_test.go similarity index 100% rename from cli/cmd/sentryctl/cmd_agents_test.go rename to cli/cmd/cairnobsctl/cmd_agents_test.go diff --git a/cli/cmd/sentryctl/cmd_alerts.go b/cli/cmd/cairnobsctl/cmd_alerts.go similarity index 77% rename from cli/cmd/sentryctl/cmd_alerts.go rename to cli/cmd/cairnobsctl/cmd_alerts.go index 7647caf..463f99a 100644 --- a/cli/cmd/sentryctl/cmd_alerts.go +++ b/cli/cmd/cairnobsctl/cmd_alerts.go @@ -8,7 +8,7 @@ import ( func cmdAlerts(args []string, stdout, stderr io.Writer) int { if len(args) == 0 { - fmt.Fprintln(stderr, "sentryctl alerts: expected a subcommand (list, get, apply)") + fmt.Fprintln(stderr, "cairnobsctl alerts: expected a subcommand (list, get, apply)") return 1 } alertingURL, rest := extractAlertingAPIFlag(args[1:], os.Getenv) @@ -19,13 +19,13 @@ func cmdAlerts(args []string, stdout, stderr io.Writer) int { return httpGetJSON(alertingURL, "/rules", token, stdout, stderr) case "get": if len(rest) == 0 { - fmt.Fprintln(stderr, "sentryctl alerts get: missing rule id") + fmt.Fprintln(stderr, "cairnobsctl alerts get: missing rule id") return 1 } return httpGetJSON(alertingURL, "/rules/"+rest[0], token, stdout, stderr) case "apply": if len(rest) == 0 { - fmt.Fprintln(stderr, "sentryctl alerts apply: missing file path") + fmt.Fprintln(stderr, "cairnobsctl alerts apply: missing file path") return 1 } // POST /rules accepts the same shape it returns -- a rule @@ -33,7 +33,7 @@ func cmdAlerts(args []string, stdout, stderr io.Writer) int { // target ID) applies directly with no reshaping. return httpPostFileJSON(alertingURL, "/rules", token, rest[0], stdout, stderr) default: - fmt.Fprintf(stderr, "sentryctl alerts: unknown subcommand %q (want list, get, apply)\n", args[0]) + fmt.Fprintf(stderr, "cairnobsctl alerts: unknown subcommand %q (want list, get, apply)\n", args[0]) return 1 } } diff --git a/cli/cmd/sentryctl/cmd_alerts_test.go b/cli/cmd/cairnobsctl/cmd_alerts_test.go similarity index 97% rename from cli/cmd/sentryctl/cmd_alerts_test.go rename to cli/cmd/cairnobsctl/cmd_alerts_test.go index 52023fd..a453c7b 100644 --- a/cli/cmd/sentryctl/cmd_alerts_test.go +++ b/cli/cmd/cairnobsctl/cmd_alerts_test.go @@ -28,7 +28,7 @@ func TestExtractAlertingAPIFlagOverride(t *testing.T) { func TestExtractAlertingAPIFlagFromEnv(t *testing.T) { alertingURL, _ := extractAlertingAPIFlag(nil, func(k string) string { - if k == "SENTRYCTL_ALERTING_API_URL" { + if k == "CAIRNOBSCTL_ALERTING_API_URL" { return "http://env-alerting:8081" } return "" diff --git a/cli/cmd/sentryctl/cmd_dashboards.go b/cli/cmd/cairnobsctl/cmd_dashboards.go similarity index 72% rename from cli/cmd/sentryctl/cmd_dashboards.go rename to cli/cmd/cairnobsctl/cmd_dashboards.go index 53a122e..18d2ed6 100644 --- a/cli/cmd/sentryctl/cmd_dashboards.go +++ b/cli/cmd/cairnobsctl/cmd_dashboards.go @@ -9,7 +9,7 @@ import ( func cmdDashboards(args []string, stdout, stderr io.Writer) int { if len(args) == 0 { - fmt.Fprintln(stderr, "sentryctl dashboards: expected a subcommand (list, get, apply, permissions)") + fmt.Fprintln(stderr, "cairnobsctl dashboards: expected a subcommand (list, get, apply, permissions)") return 1 } apiURL, rest := extractAPIFlag(args[1:], os.Getenv) @@ -20,13 +20,13 @@ func cmdDashboards(args []string, stdout, stderr io.Writer) int { return httpGetJSON(apiURL, "/dashboards", token, stdout, stderr) case "get": if len(rest) == 0 { - fmt.Fprintln(stderr, "sentryctl dashboards get: missing dashboard id") + fmt.Fprintln(stderr, "cairnobsctl dashboards get: missing dashboard id") return 1 } return httpGetJSON(apiURL, "/dashboards/"+rest[0], token, stdout, stderr) case "apply": if len(rest) == 0 { - fmt.Fprintln(stderr, "sentryctl dashboards apply: missing file path") + fmt.Fprintln(stderr, "cairnobsctl dashboards apply: missing file path") return 1 } // The import endpoint consumes exactly the shape GET @@ -35,12 +35,12 @@ func cmdDashboards(args []string, stdout, stderr io.Writer) int { return httpPostFileJSON(apiURL, "/dashboards/import", token, rest[0], stdout, stderr) case "permissions": if len(rest) == 0 { - fmt.Fprintln(stderr, "sentryctl dashboards permissions: expected a subcommand (list, grant, revoke)") + fmt.Fprintln(stderr, "cairnobsctl dashboards permissions: expected a subcommand (list, grant, revoke)") return 1 } return cmdDashboardsPermissions(rest, apiURL, token, stdout, stderr) default: - fmt.Fprintf(stderr, "sentryctl dashboards: unknown subcommand %q (want list, get, apply, permissions)\n", args[0]) + fmt.Fprintf(stderr, "cairnobsctl dashboards: unknown subcommand %q (want list, get, apply, permissions)\n", args[0]) return 1 } } @@ -49,8 +49,8 @@ func cmdDashboards(args []string, stdout, stderr io.Writer) int { // surface -- PUT/DELETE /dashboards/{id}/permissions/{userId} existed // with no caller but Go tests and curl until now (see // /docs/phase-4-runbook.md's "Known gaps"). Kept as dashboards' -// own sub-subcommand rather than a flat sentryctl command (like -// "sentryctl dashboard-permissions grant ...") since a grant only ever +// own sub-subcommand rather than a flat cairnobsctl command (like +// "cairnobsctl dashboard-permissions grant ...") since a grant only ever // makes sense in the context of one specific dashboard -- args[0] // selects list/grant/revoke. func cmdDashboardsPermissions(args []string, apiURL, token string, stdout, stderr io.Writer) int { @@ -58,13 +58,13 @@ func cmdDashboardsPermissions(args []string, apiURL, token string, stdout, stder switch sub { case "list": if len(rest) == 0 { - fmt.Fprintln(stderr, "sentryctl dashboards permissions list: missing dashboard id") + fmt.Fprintln(stderr, "cairnobsctl dashboards permissions list: missing dashboard id") return 1 } return httpGetJSON(apiURL, "/dashboards/"+rest[0]+"/permissions", token, stdout, stderr) case "grant": if len(rest) < 3 { - fmt.Fprintln(stderr, "sentryctl dashboards permissions grant: usage: grant ") + fmt.Fprintln(stderr, "cairnobsctl dashboards permissions grant: usage: grant ") return 1 } dashboardID, userID, role := rest[0], rest[1], rest[2] @@ -73,7 +73,7 @@ func cmdDashboardsPermissions(args []string, apiURL, token string, stdout, stder // only ever raises someone as high as Editor; the server // rejects anything else too, this just fails faster/locally. if role != "viewer" && role != "editor" { - fmt.Fprintf(stderr, "sentryctl dashboards permissions grant: role must be \"viewer\" or \"editor\", got %q\n", role) + fmt.Fprintf(stderr, "cairnobsctl dashboards permissions grant: role must be \"viewer\" or \"editor\", got %q\n", role) return 1 } body := fmt.Sprintf(`{"role":%q}`, role) @@ -81,14 +81,14 @@ func cmdDashboardsPermissions(args []string, apiURL, token string, stdout, stder return httpMutateNoBody(http.MethodPut, apiURL, path, token, body, "granted", stdout, stderr) case "revoke": if len(rest) < 2 { - fmt.Fprintln(stderr, "sentryctl dashboards permissions revoke: usage: revoke ") + fmt.Fprintln(stderr, "cairnobsctl dashboards permissions revoke: usage: revoke ") return 1 } dashboardID, userID := rest[0], rest[1] path := "/dashboards/" + dashboardID + "/permissions/" + userID return httpMutateNoBody(http.MethodDelete, apiURL, path, token, "", "revoked", stdout, stderr) default: - fmt.Fprintf(stderr, "sentryctl dashboards permissions: unknown subcommand %q (want list, grant, revoke)\n", sub) + fmt.Fprintf(stderr, "cairnobsctl dashboards permissions: unknown subcommand %q (want list, grant, revoke)\n", sub) return 1 } } diff --git a/cli/cmd/sentryctl/cmd_dashboards_test.go b/cli/cmd/cairnobsctl/cmd_dashboards_test.go similarity index 100% rename from cli/cmd/sentryctl/cmd_dashboards_test.go rename to cli/cmd/cairnobsctl/cmd_dashboards_test.go diff --git a/cli/cmd/sentryctl/cmd_ping.go b/cli/cmd/cairnobsctl/cmd_ping.go similarity index 93% rename from cli/cmd/sentryctl/cmd_ping.go rename to cli/cmd/cairnobsctl/cmd_ping.go index 78162d7..a9c4de8 100644 --- a/cli/cmd/sentryctl/cmd_ping.go +++ b/cli/cmd/cairnobsctl/cmd_ping.go @@ -9,7 +9,7 @@ import ( ) // parsePingArgs resolves the api base URL for ping: --api flag wins, then -// $SENTRYCTL_API_URL, then the hardcoded default. Kept pure (env passed in +// $CAIRNOBSCTL_API_URL, then the hardcoded default. Kept pure (env passed in // as a function) and separate from the HTTP call so it's unit-testable // without a real environment or server. func parsePingArgs(args []string, env func(string) string) string { diff --git a/cli/cmd/sentryctl/cmd_query.go b/cli/cmd/cairnobsctl/cmd_query.go similarity index 98% rename from cli/cmd/sentryctl/cmd_query.go rename to cli/cmd/cairnobsctl/cmd_query.go index 6fb6349..3f94dee 100644 --- a/cli/cmd/sentryctl/cmd_query.go +++ b/cli/cmd/cairnobsctl/cmd_query.go @@ -28,7 +28,7 @@ type queryArgs struct { // parseQueryArgs is pure (env passed in, no I/O), same testability // reasoning as parsePingArgs. Non-flag arguments are joined with spaces -// to form the query, so `sentryctl query service=api status=500` (no +// to form the query, so `cairnobsctl query service=api status=500` (no // quotes, no shell-special characters) works without requiring users to // quote every query -- though anything using "|" still needs shell // quoting regardless, since that's a real shell pipe character otherwise. @@ -97,7 +97,7 @@ func cmdQuery(args []string, stdout, stderr io.Writer) int { } if strings.TrimSpace(qa.query) == "" { - fmt.Fprintln(stderr, "sentryctl query: missing query string") + fmt.Fprintln(stderr, "cairnobsctl query: missing query string") return 1 } return runAndPrintQuery(qa.apiURL, qa.query, qa.language, qa.jsonOut, stdout, stderr) diff --git a/cli/cmd/sentryctl/cmd_query_test.go b/cli/cmd/cairnobsctl/cmd_query_test.go similarity index 100% rename from cli/cmd/sentryctl/cmd_query_test.go rename to cli/cmd/cairnobsctl/cmd_query_test.go diff --git a/cli/cmd/sentryctl/cmd_users.go b/cli/cmd/cairnobsctl/cmd_users.go similarity index 89% rename from cli/cmd/sentryctl/cmd_users.go rename to cli/cmd/cairnobsctl/cmd_users.go index 961e3ef..36ef2f4 100644 --- a/cli/cmd/sentryctl/cmd_users.go +++ b/cli/cmd/cairnobsctl/cmd_users.go @@ -3,7 +3,7 @@ // runbook, and api/localauth's package doc comment for the full // feature). Same list/create/delete shape as agents/dashboards, plus a // "login" subcommand: unlike every other resource this CLI manages, -// there's no way to get a first SENTRYCTL_TOKEN without one. +// there's no way to get a first CAIRNOBSCTL_TOKEN without one. package main import ( @@ -18,7 +18,7 @@ import ( func cmdUsers(args []string, stdin io.Reader, stdout, stderr io.Writer) int { if len(args) == 0 { - fmt.Fprintln(stderr, "sentryctl users: expected a subcommand (login, list, create, delete, reset-password)") + fmt.Fprintln(stderr, "cairnobsctl users: expected a subcommand (login, list, create, delete, reset-password)") return 1 } apiURL, rest := extractAPIFlag(args[1:], os.Getenv) @@ -27,7 +27,7 @@ func cmdUsers(args []string, stdin io.Reader, stdout, stderr io.Writer) int { switch args[0] { case "login": if len(rest) == 0 { - fmt.Fprintln(stderr, "sentryctl users login: missing username") + fmt.Fprintln(stderr, "cairnobsctl users login: missing username") return 1 } return cmdUsersLogin(rest[0], rest[1:], apiURL, stdin, stdout, stderr) @@ -35,24 +35,24 @@ func cmdUsers(args []string, stdin io.Reader, stdout, stderr io.Writer) int { return httpGetJSON(apiURL, "/auth/users", token, stdout, stderr) case "create": if len(rest) == 0 { - fmt.Fprintln(stderr, "sentryctl users create: missing username") + fmt.Fprintln(stderr, "cairnobsctl users create: missing username") return 1 } return cmdUsersCreate(rest[0], rest[1:], apiURL, token, stdin, stdout, stderr) case "delete": if len(rest) == 0 { - fmt.Fprintln(stderr, "sentryctl users delete: missing user id") + fmt.Fprintln(stderr, "cairnobsctl users delete: missing user id") return 1 } return httpMutateNoBody(http.MethodDelete, apiURL, "/auth/users/"+rest[0], token, "", "user deleted", stdout, stderr) case "reset-password": if len(rest) == 0 { - fmt.Fprintln(stderr, "sentryctl users reset-password: missing user id") + fmt.Fprintln(stderr, "cairnobsctl users reset-password: missing user id") return 1 } return cmdUsersResetPassword(rest[0], rest[1:], apiURL, token, stdin, stdout, stderr) default: - fmt.Fprintf(stderr, "sentryctl users: unknown subcommand %q (want login, list, create, delete, reset-password)\n", args[0]) + fmt.Fprintf(stderr, "cairnobsctl users: unknown subcommand %q (want login, list, create, delete, reset-password)\n", args[0]) return 1 } } @@ -81,7 +81,7 @@ func extractPasswordStdinFlag(args []string) (useStdin bool, rest []string) { // readPasswordFromStdin reads a single line from stdin. Not masked // (this codebase has no terminal/raw-mode dependency to draw on -- see // resolveToken's doc comment for the same tradeoff already accepted for -// SENTRYCTL_TOKEN); pipe the value in (`echo "$PW" | sentryctl users +// CAIRNOBSCTL_TOKEN); pipe the value in (`echo "$PW" | cairnobsctl users // login admin`) rather than typing it at an interactive terminal where // that matters. func readPasswordFromStdin(stdin io.Reader) (string, error) { @@ -103,7 +103,7 @@ type loginResponseBody struct { } // cmdUsersLogin prints only the raw token to stdout on success (nothing -// else) -- deliberately pipeable: `export SENTRYCTL_TOKEN=$(sentryctl +// else) -- deliberately pipeable: `export CAIRNOBSCTL_TOKEN=$(cairnobsctl // users login admin)`. func cmdUsersLogin(username string, _ []string, apiURL string, stdin io.Reader, stdout, stderr io.Writer) int { password, err := readPasswordFromStdin(stdin) diff --git a/cli/cmd/sentryctl/cmd_users_test.go b/cli/cmd/cairnobsctl/cmd_users_test.go similarity index 98% rename from cli/cmd/sentryctl/cmd_users_test.go rename to cli/cmd/cairnobsctl/cmd_users_test.go index 2008127..e12c693 100644 --- a/cli/cmd/sentryctl/cmd_users_test.go +++ b/cli/cmd/cairnobsctl/cmd_users_test.go @@ -39,7 +39,7 @@ func TestCmdUsersLoginPrintsOnlyTheToken(t *testing.T) { t.Fatalf("code = %d, want 0; stderr=%s", code, stderr.String()) } if got := strings.TrimSpace(stdout.String()); got != "abc123" { - t.Fatalf("stdout = %q, want exactly the raw token (pipeable into SENTRYCTL_TOKEN)", got) + t.Fatalf("stdout = %q, want exactly the raw token (pipeable into CAIRNOBSCTL_TOKEN)", got) } } @@ -59,7 +59,7 @@ func TestCmdUsersLoginFailure(t *testing.T) { t.Fatalf("stderr = %q, want it to surface the server's error message", stderr.String()) } if stdout.String() != "" { - t.Fatalf("stdout = %q, want empty on failure (nothing pipeable into SENTRYCTL_TOKEN)", stdout.String()) + t.Fatalf("stdout = %q, want empty on failure (nothing pipeable into CAIRNOBSCTL_TOKEN)", stdout.String()) } } diff --git a/cli/cmd/sentryctl/httpclient.go b/cli/cmd/cairnobsctl/httpclient.go similarity index 98% rename from cli/cmd/sentryctl/httpclient.go rename to cli/cmd/cairnobsctl/httpclient.go index 67f8fed..717c9a3 100644 --- a/cli/cmd/sentryctl/httpclient.go +++ b/cli/cmd/cairnobsctl/httpclient.go @@ -13,7 +13,7 @@ import ( var httpClient = &http.Client{Timeout: 30 * time.Second} -// setAuth attaches SENTRYCTL_TOKEN (see resolveToken) as a Bearer +// setAuth attaches CAIRNOBSCTL_TOKEN (see resolveToken) as a Bearer // credential, a no-op when token is empty -- matches every backend's // nil-authorizer no-op default (see api/internal/authz.RequireRole*). func setAuth(req *http.Request, token string) { diff --git a/cli/cmd/sentryctl/main.go b/cli/cmd/cairnobsctl/main.go similarity index 73% rename from cli/cmd/sentryctl/main.go rename to cli/cmd/cairnobsctl/main.go index 3aecb7f..8ef485d 100644 --- a/cli/cmd/sentryctl/main.go +++ b/cli/cmd/cairnobsctl/main.go @@ -1,4 +1,4 @@ -// Command sentryctl is Sentry's control CLI. Six subcommands now +// Command cairnobsctl is Cairn OBS's control CLI. Six subcommands now // (ping, query, dashboards, alerts) clearly justify splitting dispatch // across files -- see cli/README.md's "revisit once there's a real // command tree" note -- while keeping the same hand-rolled switch on @@ -46,34 +46,34 @@ func run(args []string, stdout, stderr io.Writer) int { usage(stdout) return 0 default: - fmt.Fprintf(stderr, "sentryctl: unknown command %q\n", args[0]) + fmt.Fprintf(stderr, "cairnobsctl: unknown command %q\n", args[0]) usage(stderr) return 1 } } func usage(w io.Writer) { - fmt.Fprintln(w, `sentryctl: Sentry control CLI + fmt.Fprintln(w, `cairnobsctl: Cairn OBS control CLI Usage: - sentryctl ping [--api ] - sentryctl query "" [--api ] [--language sql|spl] [--json] - sentryctl dashboards list|get |apply [--api ] - sentryctl dashboards permissions list [--api ] - sentryctl dashboards permissions grant viewer|editor [--api ] - sentryctl dashboards permissions revoke [--api ] - sentryctl alerts list|get |apply [--alerting-api ] - sentryctl agents list|get [--api ] - sentryctl agents config get |clear [--api ] - sentryctl agents config set [--batch-max-size N] [--batch-flush-interval-ms N] + cairnobsctl ping [--api ] + cairnobsctl query "" [--api ] [--language sql|spl] [--json] + cairnobsctl dashboards list|get |apply [--api ] + cairnobsctl dashboards permissions list [--api ] + cairnobsctl dashboards permissions grant viewer|editor [--api ] + cairnobsctl dashboards permissions revoke [--api ] + cairnobsctl alerts list|get |apply [--alerting-api ] + cairnobsctl agents list|get [--api ] + cairnobsctl agents config get |clear [--api ] + cairnobsctl agents config set [--batch-max-size N] [--batch-flush-interval-ms N] [--heartbeat-enabled true|false] [--heartbeat-interval-ms N] [--journald-unit UNIT] [--api ] - sentryctl agents restart [--yes] [--api ] - sentryctl users login [--password ] [--api ] - sentryctl users list [--api ] - sentryctl users create [--password ] [--role viewer|editor|admin|owner] [--api ] - sentryctl users delete [--api ] - sentryctl users reset-password [--password ] [--api ] + cairnobsctl agents restart [--yes] [--api ] + cairnobsctl users login [--password ] [--api ] + cairnobsctl users list [--api ] + cairnobsctl users create [--password ] [--role viewer|editor|admin|owner] [--api ] + cairnobsctl users delete [--api ] + cairnobsctl users reset-password [--password ] [--api ] Commands: ping Checks that the api service is reachable via GET /healthz. @@ -106,19 +106,19 @@ Commands: api/localauth -- only meaningful on a deployment with LOCAL_AUTH_ENABLED set; a 404 on any of these means it isn't). "login" is the only command that works with no - $SENTRYCTL_TOKEN set yet -- it prints just the raw token - to stdout: `+"`export SENTRYCTL_TOKEN=$(sentryctl users login admin)`"+`. + $CAIRNOBSCTL_TOKEN set yet -- it prints just the raw token + to stdout: `+"`export CAIRNOBSCTL_TOKEN=$(cairnobsctl users login admin)`"+`. --password (on any users subcommand) is read from stdin if omitted -- same shell-history/ps caveat as typing a credential in any flag, prefer piping it in. "create"/"list"/"delete"/"reset-password" require an owner-role token (see RegisterRoutes in api/localauth). ---api defaults to $SENTRYCTL_API_URL, or `+defaultAPIURL+` if unset. ---alerting-api defaults to $SENTRYCTL_ALERTING_API_URL, or `+defaultAlertingURL+` if unset. +--api defaults to $CAIRNOBSCTL_API_URL, or `+defaultAPIURL+` if unset. +--alerting-api defaults to $CAIRNOBSCTL_ALERTING_API_URL, or `+defaultAlertingURL+` if unset. --language overrides auto-detection; omit it for the common case. -$SENTRYCTL_TOKEN, if set, is sent as "Authorization: Bearer " on +$CAIRNOBSCTL_TOKEN, if set, is sent as "Authorization: Bearer " on every request -- required once a deployment configures enterprise-auth (see /docs/phase-4-rbac-design.md). No flag equivalent, deliberately: unlike --api, a credential shouldn't be typed where shell history or @@ -126,25 +126,25 @@ unlike --api, a credential shouldn't be typed where shell history or } func resolveAPIURL(env func(string) string) string { - if v := env("SENTRYCTL_API_URL"); v != "" { + if v := env("CAIRNOBSCTL_API_URL"); v != "" { return v } return defaultAPIURL } func resolveAlertingURL(env func(string) string) string { - if v := env("SENTRYCTL_ALERTING_API_URL"); v != "" { + if v := env("CAIRNOBSCTL_ALERTING_API_URL"); v != "" { return v } return defaultAlertingURL } -// resolveToken reads the RoleService/human bearer credential sentryctl +// resolveToken reads the RoleService/human bearer credential cairnobsctl // presents to api/alerting once enterprise-auth enforcement is turned // on (api/internal/authz.RequireRole*) -- empty by default, matching // every other Phase 0-3 client's nil-authorizer no-op behavior. func resolveToken(env func(string) string) string { - return env("SENTRYCTL_TOKEN") + return env("CAIRNOBSCTL_TOKEN") } type errorResponseBody struct { diff --git a/cli/cmd/sentryctl/main_test.go b/cli/cmd/cairnobsctl/main_test.go similarity index 99% rename from cli/cmd/sentryctl/main_test.go rename to cli/cmd/cairnobsctl/main_test.go index d4cbe58..9312e89 100644 --- a/cli/cmd/sentryctl/main_test.go +++ b/cli/cmd/cairnobsctl/main_test.go @@ -18,7 +18,7 @@ func TestParsePingArgsDefault(t *testing.T) { func TestParsePingArgsFromEnv(t *testing.T) { env := func(k string) string { - if k == "SENTRYCTL_API_URL" { + if k == "CAIRNOBSCTL_API_URL" { return "http://env-host:1234" } return "" @@ -30,7 +30,7 @@ func TestParsePingArgsFromEnv(t *testing.T) { func TestParsePingArgsFlagOverridesEnv(t *testing.T) { env := func(k string) string { - if k == "SENTRYCTL_API_URL" { + if k == "CAIRNOBSCTL_API_URL" { return "http://env-host:1234" } return "" diff --git a/cli/go.mod b/cli/go.mod index ea464ef..0ad6ab1 100644 --- a/cli/go.mod +++ b/cli/go.mod @@ -1,3 +1,3 @@ -module github.com/sentry/sentry/cli +module github.com/cairnobs/cairnobs/cli go 1.25 diff --git a/deploy/README.md b/deploy/README.md index 6ac32f9..1d2cb24 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -1,14 +1,14 @@ # deploy -Kubernetes deployment for Sentry, added in Phase 4 (`/deploy` was +Kubernetes deployment for Cairn OBS, added in Phase 4 (`/deploy` was deliberately stubbed through Phase 3 -- see `/CLAUDE.md`'s Phase 3 non-goals). Two pieces: - `operator/` -- a small Go controller-runtime Operator managing one CRD (`Tenant`). See `operator/README.md`. -- `helm/sentry/` -- a Helm chart covering every `docker-compose.yml` +- `helm/cairnobs/` -- a Helm chart covering every `docker-compose.yml` service, plus the operator and `Tenant` CRs when - `enterprise.enabled=true`. See `helm/sentry/README.md`. + `enterprise.enabled=true`. See `helm/cairnobs/README.md`. ## What "multi-tenant-aware" means here, precisely @@ -29,7 +29,7 @@ add: (`values.tenants`) alongside the rest of the stack, and swaps `api`'s Deployment for `enterprise-api`'s whenever `enterprise.enabled` is true, so which query binary actually serves traffic is no longer a - separately-forgettable decision (see `helm/sentry/README.md`'s "`api` + separately-forgettable decision (see `helm/cairnobs/README.md`'s "`api` vs `enterprise-api`" section). **Now unified, in a deliberately lightweight way**: `enterprise-api @@ -65,7 +65,7 @@ gap, which was two *disconnected* sources of truth, not two actors). `kubectl`/`helm` were installed without root (static binaries into `~/.local/bin`), a real local cluster was created, every image this chart references was built and loaded into it, and the full two-tenant -walkthrough (`helm/sentry/README.md`) was run end to end -- both tenants +walkthrough (`helm/cairnobs/README.md`) was run end to end -- both tenants reached `Tenant.status.phase: Active` with real generated ClickHouse credentials in their Secrets. See `/docs/phase-4-runbook.md` §7 for the exact commands and the two real chart bugs this run found and fixed @@ -88,16 +88,16 @@ true, kept as additional evidence, not superseded by the above): `k8s.io/client-go`'s fake dynamic and typed clientsets -- real client library, fake transport, same shape as `enterprise/internal/ searchclient`'s in-process gRPC tests. What this doesn't prove: that - `sentry.io/v1alpha1.Tenant`'s real CRD schema (a real apiserver's + `cairnobs.io/v1alpha1.Tenant`'s real CRD schema (a real apiserver's OpenAPI validation) accepts exactly what this package writes -- the `helm template`/kubeconform check below covers the schema shape, not a live write against it. -- `deploy/operator/config/crd/sentry.io_tenants.yaml`: parsed with +- `deploy/operator/config/crd/cairnobs.io_tenants.yaml`: parsed with `sigs.k8s.io/yaml` + strict-unmarshaled into the real `k8s.io/apiextensions-apiserver` `CustomResourceDefinition` Go type -- catches YAML syntax errors and structural mistakes, not a live-cluster admission check. -- `deploy/helm/sentry`: `helm lint` passes; `helm template` renders +- `deploy/helm/cairnobs`: `helm lint` passes; `helm template` renders cleanly under both default values and a `enterprise.enabled: true` + two-tenant override; the rendered output was checked with `kubeconform -strict` against the real Kubernetes 1.31 OpenAPI schema for every @@ -107,7 +107,7 @@ true, kept as additional evidence, not superseded by the above): together on a live cluster (Job/StatefulSet startup ordering, PVC provisioning, actual pod scheduling). Specifically confirmed by parsing the rendered YAML (not just eyeballing it): exactly one - `Deployment`/`Service` named `sentry-api` renders in each mode, with + `Deployment`/`Service` named `cairnobs-api` renders in each mode, with the `enterprise.enabled: true` render using the `enterprise-api` image and the default render using plain `api`'s. Also confirmed for the `tenantOperator.enabled: true` case: `enterprise-api` gets its own diff --git a/deploy/helm/sentry/Chart.yaml b/deploy/helm/cairnobs/Chart.yaml similarity index 81% rename from deploy/helm/sentry/Chart.yaml rename to deploy/helm/cairnobs/Chart.yaml index 091412e..41845de 100644 --- a/deploy/helm/sentry/Chart.yaml +++ b/deploy/helm/cairnobs/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 -name: sentry +name: cairnobs description: >- - Sentry: Kubernetes-native distributed log aggregation. Phase 4 adds + Cairn OBS: Kubernetes-native distributed log aggregation. Phase 4 adds multi-tenant-aware deployment (per-tenant ClickHouse credential Secrets via the tenant-operator, optional enterprise-auth) on top of the same single-cluster topology Phases 0-3 ran under docker-compose -- see diff --git a/deploy/helm/sentry/README.md b/deploy/helm/cairnobs/README.md similarity index 93% rename from deploy/helm/sentry/README.md rename to deploy/helm/cairnobs/README.md index ee9ec65..80a1966 100644 --- a/deploy/helm/sentry/README.md +++ b/deploy/helm/cairnobs/README.md @@ -1,4 +1,4 @@ -# deploy/helm/sentry +# deploy/helm/cairnobs A Helm chart covering every `docker-compose.yml` service (Redpanda, ClickHouse, Postgres, ingest, search, alerting, web) plus, when @@ -49,7 +49,7 @@ be reachable). This chart approximates that more loosely: up yet. - App Deployments get an `initContainer` that busy-waits for their dependency's **TCP port**, not for a specific Job's completion (see - `templates/_helpers.tpl`'s `sentry.waitForTCP`) -- this covers "is + `templates/_helpers.tpl`'s `cairnobs.waitForTCP`) -- this covers "is ClickHouse/Postgres/Redpanda up" but not "has the migration Job actually finished." - The gap that leaves (a pod starts before its migration has completed) @@ -68,7 +68,7 @@ be reachable). This chart approximates that more loosely: # here -- that's a helm template-only flag (install always installs # crds/ by default); confirmed the hard way running this against a real # kind cluster, see /docs/phase-4-runbook.md §7. -helm install sentry . \ +helm install cairnobs . \ --set enterprise.enabled=true \ --set tenantOperator.enabled=true \ --set 'tenants[0].name=acme' --set 'tenants[0].displayName=Acme Corp' \ @@ -79,12 +79,12 @@ kubectl get tenants # declarative request; nothing has actually provisioned ClickHouse for # either yet (see below). -kubectl exec -it deploy/sentry-api -- /enterprise-api -provision-tenant=acme -display-name="Acme Corp" -kubectl exec -it deploy/sentry-api -- /enterprise-api -provision-tenant=globex -display-name="Globex Corporation" +kubectl exec -it deploy/cairnobs-api -- /enterprise-api -provision-tenant=acme -display-name="Acme Corp" +kubectl exec -it deploy/cairnobs-api -- /enterprise-api -provision-tenant=globex -display-name="Globex Corporation" kubectl get tenants # expect: both Active now. -kubectl get secret sentry-tenant-acme-clickhouse sentry-tenant-globex-clickhouse +kubectl get secret cairnobs-tenant-acme-clickhouse cairnobs-tenant-globex-clickhouse ``` Before any of this: `ingest` needs a real mTLS cert Secret @@ -135,7 +135,7 @@ actually reachable from a browser before pointing real users at it. ```sh helm lint . -helm template sentry . --include-crds > /tmp/rendered.yaml +helm template cairnobs . --include-crds > /tmp/rendered.yaml ``` See `/deploy/README.md`'s verification section for what was checked diff --git a/deploy/helm/sentry/crds/sentry.io_tenants.yaml b/deploy/helm/cairnobs/crds/cairnobs.io_tenants.yaml similarity index 98% rename from deploy/helm/sentry/crds/sentry.io_tenants.yaml rename to deploy/helm/cairnobs/crds/cairnobs.io_tenants.yaml index 5120cf0..578eaa3 100644 --- a/deploy/helm/sentry/crds/sentry.io_tenants.yaml +++ b/deploy/helm/cairnobs/crds/cairnobs.io_tenants.yaml @@ -7,9 +7,9 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: - name: tenants.sentry.io + name: tenants.cairnobs.io spec: - group: sentry.io + group: cairnobs.io names: kind: Tenant listKind: TenantList diff --git a/deploy/helm/sentry/templates/_helpers.tpl b/deploy/helm/cairnobs/templates/_helpers.tpl similarity index 82% rename from deploy/helm/sentry/templates/_helpers.tpl rename to deploy/helm/cairnobs/templates/_helpers.tpl index c54fc4a..dced8a0 100644 --- a/deploy/helm/sentry/templates/_helpers.tpl +++ b/deploy/helm/cairnobs/templates/_helpers.tpl @@ -1,31 +1,31 @@ {{/* Standard labels applied to every resource this chart renders. */}} -{{- define "sentry.labels" -}} -app.kubernetes.io/part-of: sentry +{{- define "cairnobs.labels" -}} +app.kubernetes.io/part-of: cairnobs app.kubernetes.io/managed-by: {{ .Release.Service }} helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }} {{- end -}} {{/* Per-component selector labels -- usage: -{{ include "sentry.selectorLabels" (list $ "api") }} +{{ include "cairnobs.selectorLabels" (list $ "api") }} A plain string arg (the old shape this started with) can't reach $.Release from inside the defined template -- `include`'s argument becomes the template's entire root context, so a bare "api" string leaves no way to get back to the chart root. A two-element list carries both. */}} -{{- define "sentry.selectorLabels" -}} +{{- define "cairnobs.selectorLabels" -}} {{- $root := index . 0 -}} {{- $name := index . 1 -}} -app.kubernetes.io/name: sentry-{{ $name }} +app.kubernetes.io/name: cairnobs-{{ $name }} app.kubernetes.io/instance: {{ $root.Release.Name }} {{- end -}} {{/* An initContainer that busy-waits for a TCP host:port to accept -connections -- usage: {{ include "sentry.waitForTCP" (list "name-suffix" "host" "port") }} +connections -- usage: {{ include "cairnobs.waitForTCP" (list "name-suffix" "host" "port") }} This approximates docker-compose.yml's `depends_on: condition: service_healthy` (waits for the dependency's process to be reachable), @@ -34,7 +34,7 @@ one-shot Job, like clickhouse-migrate, to have actually finished). That second guarantee doesn't have a lightweight equivalent here without giving every app pod's ServiceAccount RBAC to read Job status, which is a lot of privilege for a startup-ordering nicety -- see -deploy/helm/sentry/README.md's "Startup ordering" section. The gap it +deploy/helm/cairnobs/README.md's "Startup ordering" section. The gap it leaves (a pod starts before its migration Job has finished) is covered by the app's own crash-and-restart-on-connect/schema failure: every Go service here already os.Exit(1)s on a failed Postgres/ClickHouse ping at @@ -42,7 +42,7 @@ startup (see e.g. api/cmd/api/main.go), so Kubernetes' restart policy naturally retries until the schema is ready. Documented as a real, accepted tradeoff, not implied to be a hard ordering guarantee. */}} -{{- define "sentry.waitForTCP" -}} +{{- define "cairnobs.waitForTCP" -}} {{- $name := index . 0 -}} {{- $host := index . 1 -}} {{- $port := index . 2 -}} diff --git a/deploy/helm/sentry/templates/alerting.yaml b/deploy/helm/cairnobs/templates/alerting.yaml similarity index 77% rename from deploy/helm/sentry/templates/alerting.yaml rename to deploy/helm/cairnobs/templates/alerting.yaml index 3448ec8..9035e73 100644 --- a/deploy/helm/sentry/templates/alerting.yaml +++ b/deploy/helm/cairnobs/templates/alerting.yaml @@ -3,22 +3,22 @@ kind: Deployment metadata: name: {{ .Release.Name }}-alerting labels: - {{- include "sentry.labels" . | nindent 4 }} - {{- include "sentry.selectorLabels" (list $ "alerting") | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "alerting") | nindent 4 }} spec: # See values.yaml's comment: replicas is not a real knob here yet. replicas: {{ .Values.alerting.replicas }} selector: matchLabels: - {{- include "sentry.selectorLabels" (list $ "alerting") | nindent 6 }} + {{- include "cairnobs.selectorLabels" (list $ "alerting") | nindent 6 }} template: metadata: labels: - {{- include "sentry.selectorLabels" (list $ "alerting") | nindent 8 }} + {{- include "cairnobs.selectorLabels" (list $ "alerting") | nindent 8 }} spec: initContainers: - {{- include "sentry.waitForTCP" (list "postgres" (printf "%s-postgres" .Release.Name) "5432") | nindent 8 }} - {{- include "sentry.waitForTCP" (list "api" (printf "%s-api" .Release.Name) "8080") | nindent 8 }} + {{- include "cairnobs.waitForTCP" (list "postgres" (printf "%s-postgres" .Release.Name) "5432") | nindent 8 }} + {{- include "cairnobs.waitForTCP" (list "api" (printf "%s-api" .Release.Name) "8080") | nindent 8 }} containers: - name: alerting image: "{{ .Values.alerting.image.repository }}:{{ .Values.alerting.image.tag }}" @@ -71,11 +71,11 @@ kind: Service metadata: name: {{ .Release.Name }}-alerting labels: - {{- include "sentry.labels" . | nindent 4 }} - {{- include "sentry.selectorLabels" (list $ "alerting") | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "alerting") | nindent 4 }} spec: selector: - {{- include "sentry.selectorLabels" (list $ "alerting") | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "alerting") | nindent 4 }} ports: - name: http port: 8081 diff --git a/deploy/helm/sentry/templates/api.yaml b/deploy/helm/cairnobs/templates/api.yaml similarity index 75% rename from deploy/helm/sentry/templates/api.yaml rename to deploy/helm/cairnobs/templates/api.yaml index ced2684..fbfb9a5 100644 --- a/deploy/helm/sentry/templates/api.yaml +++ b/deploy/helm/cairnobs/templates/api.yaml @@ -13,22 +13,22 @@ kind: Deployment metadata: name: {{ .Release.Name }}-api labels: - {{- include "sentry.labels" . | nindent 4 }} - {{- include "sentry.selectorLabels" (list $ "api") | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "api") | nindent 4 }} spec: replicas: {{ .Values.api.replicas }} selector: matchLabels: - {{- include "sentry.selectorLabels" (list $ "api") | nindent 6 }} + {{- include "cairnobs.selectorLabels" (list $ "api") | nindent 6 }} template: metadata: labels: - {{- include "sentry.selectorLabels" (list $ "api") | nindent 8 }} + {{- include "cairnobs.selectorLabels" (list $ "api") | nindent 8 }} spec: initContainers: - {{- include "sentry.waitForTCP" (list "clickhouse" (printf "%s-clickhouse" .Release.Name) "9000") | nindent 8 }} - {{- include "sentry.waitForTCP" (list "postgres" (printf "%s-postgres" .Release.Name) "5432") | nindent 8 }} - {{- include "sentry.waitForTCP" (list "search" (printf "%s-search" .Release.Name) "50052") | nindent 8 }} + {{- include "cairnobs.waitForTCP" (list "clickhouse" (printf "%s-clickhouse" .Release.Name) "9000") | nindent 8 }} + {{- include "cairnobs.waitForTCP" (list "postgres" (printf "%s-postgres" .Release.Name) "5432") | nindent 8 }} + {{- include "cairnobs.waitForTCP" (list "search" (printf "%s-search" .Release.Name) "50052") | nindent 8 }} containers: - name: api image: "{{ .Values.api.image.repository }}:{{ .Values.api.image.tag }}" @@ -75,11 +75,11 @@ kind: Service metadata: name: {{ .Release.Name }}-api labels: - {{- include "sentry.labels" . | nindent 4 }} - {{- include "sentry.selectorLabels" (list $ "api") | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "api") | nindent 4 }} spec: selector: - {{- include "sentry.selectorLabels" (list $ "api") | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "api") | nindent 4 }} ports: - name: http port: 8080 diff --git a/deploy/helm/sentry/templates/clickhouse.yaml b/deploy/helm/cairnobs/templates/clickhouse.yaml similarity index 82% rename from deploy/helm/sentry/templates/clickhouse.yaml rename to deploy/helm/cairnobs/templates/clickhouse.yaml index 9166aeb..41868c7 100644 --- a/deploy/helm/sentry/templates/clickhouse.yaml +++ b/deploy/helm/cairnobs/templates/clickhouse.yaml @@ -3,18 +3,18 @@ kind: StatefulSet metadata: name: {{ .Release.Name }}-clickhouse labels: - {{- include "sentry.labels" . | nindent 4 }} - {{- include "sentry.selectorLabels" (list $ "clickhouse") | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "clickhouse") | nindent 4 }} spec: serviceName: {{ .Release.Name }}-clickhouse replicas: 1 selector: matchLabels: - {{- include "sentry.selectorLabels" (list $ "clickhouse") | nindent 6 }} + {{- include "cairnobs.selectorLabels" (list $ "clickhouse") | nindent 6 }} template: metadata: labels: - {{- include "sentry.selectorLabels" (list $ "clickhouse") | nindent 8 }} + {{- include "cairnobs.selectorLabels" (list $ "clickhouse") | nindent 8 }} spec: containers: - name: clickhouse @@ -69,12 +69,12 @@ kind: Service metadata: name: {{ .Release.Name }}-clickhouse labels: - {{- include "sentry.labels" . | nindent 4 }} - {{- include "sentry.selectorLabels" (list $ "clickhouse") | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "clickhouse") | nindent 4 }} spec: clusterIP: None selector: - {{- include "sentry.selectorLabels" (list $ "clickhouse") | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "clickhouse") | nindent 4 }} ports: - name: http port: 8123 @@ -84,19 +84,19 @@ spec: # One-shot: applies /storage/migrations/*.sql -- same image # storage/Dockerfile builds for docker-compose.yml's clickhouse-migrate # service. Plain Job, not a Helm hook -- see redpanda.yaml's comment and -# deploy/helm/sentry/README.md's "Startup ordering" section. +# deploy/helm/cairnobs/README.md's "Startup ordering" section. apiVersion: batch/v1 kind: Job metadata: name: {{ .Release.Name }}-clickhouse-migrate labels: - {{- include "sentry.labels" . | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} spec: backoffLimit: 6 template: metadata: labels: - {{- include "sentry.selectorLabels" (list $ "clickhouse-migrate") | nindent 8 }} + {{- include "cairnobs.selectorLabels" (list $ "clickhouse-migrate") | nindent 8 }} spec: restartPolicy: OnFailure containers: diff --git a/deploy/helm/sentry/templates/enterprise-api.yaml b/deploy/helm/cairnobs/templates/enterprise-api.yaml similarity index 83% rename from deploy/helm/sentry/templates/enterprise-api.yaml rename to deploy/helm/cairnobs/templates/enterprise-api.yaml index 9e23107..1b25796 100644 --- a/deploy/helm/sentry/templates/enterprise-api.yaml +++ b/deploy/helm/cairnobs/templates/enterprise-api.yaml @@ -25,19 +25,19 @@ kind: ServiceAccount metadata: name: {{ .Release.Name }}-enterprise-api labels: - {{- include "sentry.labels" . | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: {{ .Release.Name }}-enterprise-api labels: - {{- include "sentry.labels" . | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} rules: - - apiGroups: ["sentry.io"] + - apiGroups: ["cairnobs.io"] resources: ["tenants"] verbs: ["get", "list", "create"] - - apiGroups: ["sentry.io"] + - apiGroups: ["cairnobs.io"] resources: ["tenants/status"] verbs: ["get", "update", "patch"] - apiGroups: [""] @@ -49,7 +49,7 @@ kind: RoleBinding metadata: name: {{ .Release.Name }}-enterprise-api labels: - {{- include "sentry.labels" . | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} roleRef: apiGroup: rbac.authorization.k8s.io kind: Role @@ -65,27 +65,27 @@ kind: Deployment metadata: name: {{ .Release.Name }}-api labels: - {{- include "sentry.labels" . | nindent 4 }} - {{- include "sentry.selectorLabels" (list $ "api") | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "api") | nindent 4 }} app.kubernetes.io/component: enterprise-api spec: replicas: {{ .Values.api.replicas }} selector: matchLabels: - {{- include "sentry.selectorLabels" (list $ "api") | nindent 6 }} + {{- include "cairnobs.selectorLabels" (list $ "api") | nindent 6 }} template: metadata: labels: - {{- include "sentry.selectorLabels" (list $ "api") | nindent 8 }} + {{- include "cairnobs.selectorLabels" (list $ "api") | nindent 8 }} spec: {{- if .Values.tenantOperator.enabled }} serviceAccountName: {{ .Release.Name }}-enterprise-api {{- end }} initContainers: - {{- include "sentry.waitForTCP" (list "clickhouse" (printf "%s-clickhouse" .Release.Name) "9000") | nindent 8 }} - {{- include "sentry.waitForTCP" (list "postgres" (printf "%s-postgres" .Release.Name) "5432") | nindent 8 }} - {{- include "sentry.waitForTCP" (list "search" (printf "%s-search" .Release.Name) "50052") | nindent 8 }} - {{- include "sentry.waitForTCP" (list "enterprise-auth" (printf "%s-enterprise-auth" .Release.Name) "8082") | nindent 8 }} + {{- include "cairnobs.waitForTCP" (list "clickhouse" (printf "%s-clickhouse" .Release.Name) "9000") | nindent 8 }} + {{- include "cairnobs.waitForTCP" (list "postgres" (printf "%s-postgres" .Release.Name) "5432") | nindent 8 }} + {{- include "cairnobs.waitForTCP" (list "search" (printf "%s-search" .Release.Name) "50052") | nindent 8 }} + {{- include "cairnobs.waitForTCP" (list "enterprise-auth" (printf "%s-enterprise-auth" .Release.Name) "8082") | nindent 8 }} containers: - name: enterprise-api image: "{{ .Values.enterprise.apiImage.repository }}:{{ .Values.enterprise.apiImage.tag }}" @@ -161,11 +161,11 @@ kind: Service metadata: name: {{ .Release.Name }}-api labels: - {{- include "sentry.labels" . | nindent 4 }} - {{- include "sentry.selectorLabels" (list $ "api") | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "api") | nindent 4 }} spec: selector: - {{- include "sentry.selectorLabels" (list $ "api") | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "api") | nindent 4 }} ports: - name: http port: 8080 diff --git a/deploy/helm/sentry/templates/enterprise-auth.yaml b/deploy/helm/cairnobs/templates/enterprise-auth.yaml similarity index 80% rename from deploy/helm/sentry/templates/enterprise-auth.yaml rename to deploy/helm/cairnobs/templates/enterprise-auth.yaml index e1a99e3..ce083aa 100644 --- a/deploy/helm/sentry/templates/enterprise-auth.yaml +++ b/deploy/helm/cairnobs/templates/enterprise-auth.yaml @@ -4,20 +4,20 @@ kind: Deployment metadata: name: {{ .Release.Name }}-enterprise-auth labels: - {{- include "sentry.labels" . | nindent 4 }} - {{- include "sentry.selectorLabels" (list $ "enterprise-auth") | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "enterprise-auth") | nindent 4 }} spec: replicas: {{ .Values.enterprise.replicas }} selector: matchLabels: - {{- include "sentry.selectorLabels" (list $ "enterprise-auth") | nindent 6 }} + {{- include "cairnobs.selectorLabels" (list $ "enterprise-auth") | nindent 6 }} template: metadata: labels: - {{- include "sentry.selectorLabels" (list $ "enterprise-auth") | nindent 8 }} + {{- include "cairnobs.selectorLabels" (list $ "enterprise-auth") | nindent 8 }} spec: initContainers: - {{- include "sentry.waitForTCP" (list "postgres" (printf "%s-postgres" .Release.Name) "5432") | nindent 8 }} + {{- include "cairnobs.waitForTCP" (list "postgres" (printf "%s-postgres" .Release.Name) "5432") | nindent 8 }} containers: - name: enterprise-auth image: "{{ .Values.enterprise.image.repository }}:{{ .Values.enterprise.image.tag }}" @@ -76,11 +76,11 @@ kind: Service metadata: name: {{ .Release.Name }}-enterprise-auth labels: - {{- include "sentry.labels" . | nindent 4 }} - {{- include "sentry.selectorLabels" (list $ "enterprise-auth") | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "enterprise-auth") | nindent 4 }} spec: selector: - {{- include "sentry.selectorLabels" (list $ "enterprise-auth") | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "enterprise-auth") | nindent 4 }} ports: - name: http port: 8082 diff --git a/deploy/helm/sentry/templates/enterprise-ingest.yaml b/deploy/helm/cairnobs/templates/enterprise-ingest.yaml similarity index 75% rename from deploy/helm/sentry/templates/enterprise-ingest.yaml rename to deploy/helm/cairnobs/templates/enterprise-ingest.yaml index b668188..f2bc0c1 100644 --- a/deploy/helm/sentry/templates/enterprise-ingest.yaml +++ b/deploy/helm/cairnobs/templates/enterprise-ingest.yaml @@ -13,22 +13,22 @@ kind: Deployment metadata: name: {{ .Release.Name }}-enterprise-ingest labels: - {{- include "sentry.labels" . | nindent 4 }} - {{- include "sentry.selectorLabels" (list $ "enterprise-ingest") | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "enterprise-ingest") | nindent 4 }} spec: replicas: {{ .Values.ingest.replicas }} selector: matchLabels: - {{- include "sentry.selectorLabels" (list $ "enterprise-ingest") | nindent 6 }} + {{- include "cairnobs.selectorLabels" (list $ "enterprise-ingest") | nindent 6 }} template: metadata: labels: - {{- include "sentry.selectorLabels" (list $ "enterprise-ingest") | nindent 8 }} + {{- include "cairnobs.selectorLabels" (list $ "enterprise-ingest") | nindent 8 }} spec: initContainers: - {{- include "sentry.waitForTCP" (list "redpanda" (printf "%s-redpanda" .Release.Name) "9092") | nindent 8 }} - {{- include "sentry.waitForTCP" (list "clickhouse" (printf "%s-clickhouse" .Release.Name) "9000") | nindent 8 }} - {{- include "sentry.waitForTCP" (list "postgres" (printf "%s-postgres" .Release.Name) "5432") | nindent 8 }} + {{- include "cairnobs.waitForTCP" (list "redpanda" (printf "%s-redpanda" .Release.Name) "9092") | nindent 8 }} + {{- include "cairnobs.waitForTCP" (list "clickhouse" (printf "%s-clickhouse" .Release.Name) "9000") | nindent 8 }} + {{- include "cairnobs.waitForTCP" (list "postgres" (printf "%s-postgres" .Release.Name) "5432") | nindent 8 }} containers: - name: enterprise-ingest image: "{{ .Values.enterprise.ingestImage.repository }}:{{ .Values.enterprise.ingestImage.tag }}" diff --git a/deploy/helm/sentry/templates/ingest.yaml b/deploy/helm/cairnobs/templates/ingest.yaml similarity index 81% rename from deploy/helm/sentry/templates/ingest.yaml rename to deploy/helm/cairnobs/templates/ingest.yaml index a0889e0..d85d429 100644 --- a/deploy/helm/sentry/templates/ingest.yaml +++ b/deploy/helm/cairnobs/templates/ingest.yaml @@ -3,21 +3,21 @@ kind: Deployment metadata: name: {{ .Release.Name }}-ingest labels: - {{- include "sentry.labels" . | nindent 4 }} - {{- include "sentry.selectorLabels" (list $ "ingest") | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "ingest") | nindent 4 }} spec: replicas: {{ .Values.ingest.replicas }} selector: matchLabels: - {{- include "sentry.selectorLabels" (list $ "ingest") | nindent 6 }} + {{- include "cairnobs.selectorLabels" (list $ "ingest") | nindent 6 }} template: metadata: labels: - {{- include "sentry.selectorLabels" (list $ "ingest") | nindent 8 }} + {{- include "cairnobs.selectorLabels" (list $ "ingest") | nindent 8 }} spec: initContainers: - {{- include "sentry.waitForTCP" (list "redpanda" (printf "%s-redpanda" .Release.Name) "9092") | nindent 8 }} - {{- include "sentry.waitForTCP" (list "clickhouse" (printf "%s-clickhouse" .Release.Name) "9000") | nindent 8 }} + {{- include "cairnobs.waitForTCP" (list "redpanda" (printf "%s-redpanda" .Release.Name) "9092") | nindent 8 }} + {{- include "cairnobs.waitForTCP" (list "clickhouse" (printf "%s-clickhouse" .Release.Name) "9000") | nindent 8 }} containers: - name: ingest image: "{{ .Values.ingest.image.repository }}:{{ .Values.ingest.image.tag }}" @@ -68,7 +68,7 @@ spec: {{- if .Values.ingest.tlsSecretName }} volumeMounts: - name: tls - mountPath: /etc/sentry-ingest + mountPath: /etc/cairnobs-ingest readOnly: true {{- end }} resources: @@ -85,11 +85,11 @@ kind: Service metadata: name: {{ .Release.Name }}-ingest labels: - {{- include "sentry.labels" . | nindent 4 }} - {{- include "sentry.selectorLabels" (list $ "ingest") | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "ingest") | nindent 4 }} spec: selector: - {{- include "sentry.selectorLabels" (list $ "ingest") | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "ingest") | nindent 4 }} ports: - name: grpc port: 4317 diff --git a/deploy/helm/sentry/templates/postgres.yaml b/deploy/helm/cairnobs/templates/postgres.yaml similarity index 82% rename from deploy/helm/sentry/templates/postgres.yaml rename to deploy/helm/cairnobs/templates/postgres.yaml index cfaf1a1..26d39ab 100644 --- a/deploy/helm/sentry/templates/postgres.yaml +++ b/deploy/helm/cairnobs/templates/postgres.yaml @@ -3,18 +3,18 @@ kind: StatefulSet metadata: name: {{ .Release.Name }}-postgres labels: - {{- include "sentry.labels" . | nindent 4 }} - {{- include "sentry.selectorLabels" (list $ "postgres") | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "postgres") | nindent 4 }} spec: serviceName: {{ .Release.Name }}-postgres replicas: 1 selector: matchLabels: - {{- include "sentry.selectorLabels" (list $ "postgres") | nindent 6 }} + {{- include "cairnobs.selectorLabels" (list $ "postgres") | nindent 6 }} template: metadata: labels: - {{- include "sentry.selectorLabels" (list $ "postgres") | nindent 8 }} + {{- include "cairnobs.selectorLabels" (list $ "postgres") | nindent 8 }} spec: containers: - name: postgres @@ -57,12 +57,12 @@ kind: Service metadata: name: {{ .Release.Name }}-postgres labels: - {{- include "sentry.labels" . | nindent 4 }} - {{- include "sentry.selectorLabels" (list $ "postgres") | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "postgres") | nindent 4 }} spec: clusterIP: None selector: - {{- include "sentry.selectorLabels" (list $ "postgres") | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "postgres") | nindent 4 }} ports: - name: postgres port: 5432 @@ -76,13 +76,13 @@ kind: Job metadata: name: {{ .Release.Name }}-metadata-migrate labels: - {{- include "sentry.labels" . | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} spec: backoffLimit: 6 template: metadata: labels: - {{- include "sentry.selectorLabels" (list $ "metadata-migrate") | nindent 8 }} + {{- include "cairnobs.selectorLabels" (list $ "metadata-migrate") | nindent 8 }} spec: restartPolicy: OnFailure containers: diff --git a/deploy/helm/sentry/templates/redpanda.yaml b/deploy/helm/cairnobs/templates/redpanda.yaml similarity index 80% rename from deploy/helm/sentry/templates/redpanda.yaml rename to deploy/helm/cairnobs/templates/redpanda.yaml index c656b29..e1c8dfa 100644 --- a/deploy/helm/sentry/templates/redpanda.yaml +++ b/deploy/helm/cairnobs/templates/redpanda.yaml @@ -3,18 +3,18 @@ kind: StatefulSet metadata: name: {{ .Release.Name }}-redpanda labels: - {{- include "sentry.labels" . | nindent 4 }} - {{- include "sentry.selectorLabels" (list $ "redpanda") | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "redpanda") | nindent 4 }} spec: serviceName: {{ .Release.Name }}-redpanda replicas: 1 selector: matchLabels: - {{- include "sentry.selectorLabels" (list $ "redpanda") | nindent 6 }} + {{- include "cairnobs.selectorLabels" (list $ "redpanda") | nindent 6 }} template: metadata: labels: - {{- include "sentry.selectorLabels" (list $ "redpanda") | nindent 8 }} + {{- include "cairnobs.selectorLabels" (list $ "redpanda") | nindent 8 }} spec: containers: - name: redpanda @@ -60,12 +60,12 @@ kind: Service metadata: name: {{ .Release.Name }}-redpanda labels: - {{- include "sentry.labels" . | nindent 4 }} - {{- include "sentry.selectorLabels" (list $ "redpanda") | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "redpanda") | nindent 4 }} spec: clusterIP: None selector: - {{- include "sentry.selectorLabels" (list $ "redpanda") | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "redpanda") | nindent 4 }} ports: - name: kafka port: 9092 @@ -75,7 +75,7 @@ spec: # One-shot: creates the sentry.logs.raw topic. Same image # transport/Dockerfile builds for docker-compose.yml's redpanda-provision # service. Deliberately a plain Job, not a Helm hook -- see -# deploy/helm/sentry/README.md's "Startup ordering" section for why +# deploy/helm/cairnobs/README.md's "Startup ordering" section for why # (StatefulSets-as-hooks breaks helm upgrade/uninstall's ownership # tracking of stateful resources). backoffLimit gives it room to retry # until redpanda's StatefulSet is actually ready; ingest/search's own @@ -86,13 +86,13 @@ kind: Job metadata: name: {{ .Release.Name }}-redpanda-provision labels: - {{- include "sentry.labels" . | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} spec: backoffLimit: 6 template: metadata: labels: - {{- include "sentry.selectorLabels" (list $ "redpanda-provision") | nindent 8 }} + {{- include "cairnobs.selectorLabels" (list $ "redpanda-provision") | nindent 8 }} spec: restartPolicy: OnFailure containers: diff --git a/deploy/helm/sentry/templates/search.yaml b/deploy/helm/cairnobs/templates/search.yaml similarity index 69% rename from deploy/helm/sentry/templates/search.yaml rename to deploy/helm/cairnobs/templates/search.yaml index 369d35b..606d15c 100644 --- a/deploy/helm/sentry/templates/search.yaml +++ b/deploy/helm/cairnobs/templates/search.yaml @@ -3,8 +3,8 @@ kind: Deployment metadata: name: {{ .Release.Name }}-search labels: - {{- include "sentry.labels" . | nindent 4 }} - {{- include "sentry.selectorLabels" (list $ "search") | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "search") | nindent 4 }} spec: # See values.yaml's comment: replicas is not a real knob here yet. replicas: {{ .Values.search.replicas }} @@ -12,14 +12,14 @@ spec: type: Recreate # single PVC below (ReadWriteOnce) -- avoid two pods racing to mount it during a rollout selector: matchLabels: - {{- include "sentry.selectorLabels" (list $ "search") | nindent 6 }} + {{- include "cairnobs.selectorLabels" (list $ "search") | nindent 6 }} template: metadata: labels: - {{- include "sentry.selectorLabels" (list $ "search") | nindent 8 }} + {{- include "cairnobs.selectorLabels" (list $ "search") | nindent 8 }} spec: initContainers: - {{- include "sentry.waitForTCP" (list "redpanda" (printf "%s-redpanda" .Release.Name) "9092") | nindent 8 }} + {{- include "cairnobs.waitForTCP" (list "redpanda" (printf "%s-redpanda" .Release.Name) "9092") | nindent 8 }} containers: - name: search image: "{{ .Values.search.image.repository }}:{{ .Values.search.image.tag }}" @@ -36,7 +36,7 @@ spec: containerPort: 50052 volumeMounts: - name: index-data - mountPath: /var/lib/sentry-search + mountPath: /var/lib/cairnobs-search resources: {{- toYaml .Values.search.resources | nindent 12 }} volumes: @@ -49,7 +49,7 @@ kind: PersistentVolumeClaim metadata: name: {{ .Release.Name }}-search-index labels: - {{- include "sentry.labels" . | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} spec: accessModes: ["ReadWriteOnce"] resources: @@ -61,11 +61,11 @@ kind: Service metadata: name: {{ .Release.Name }}-search labels: - {{- include "sentry.labels" . | nindent 4 }} - {{- include "sentry.selectorLabels" (list $ "search") | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "search") | nindent 4 }} spec: selector: - {{- include "sentry.selectorLabels" (list $ "search") | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "search") | nindent 4 }} ports: - name: grpc port: 50052 diff --git a/deploy/helm/sentry/templates/secrets.yaml b/deploy/helm/cairnobs/templates/secrets.yaml similarity index 73% rename from deploy/helm/sentry/templates/secrets.yaml rename to deploy/helm/cairnobs/templates/secrets.yaml index 0bf2d22..93af1f4 100644 --- a/deploy/helm/sentry/templates/secrets.yaml +++ b/deploy/helm/cairnobs/templates/secrets.yaml @@ -1,6 +1,6 @@ {{/* Shared control-plane secrets -- the cluster-wide passwords -docker-compose.yml hardcodes as "sentry-dev-only"/etc (see its +docker-compose.yml hardcodes as "cairnobs-dev-only"/etc (see its clickhouse/metadata-postgres/metadata-migrate comments) become real generated-or-supplied Secrets here. Each follows the same pattern: a values override wins if set, otherwise a value is generated once and @@ -13,7 +13,7 @@ reconcileSecret). `lookup` returns nothing under `helm template` (no live cluster) -- expected; see deploy/README.md's verification section for what that means for this file specifically. */}} -{{- define "sentry.stableSecretValue" -}} +{{- define "cairnobs.stableSecretValue" -}} {{- $ns := index . 0 -}} {{- $name := index . 1 -}} {{- $key := index . 2 -}} @@ -33,7 +33,7 @@ kind: Secret metadata: name: {{ .Release.Name }}-clickhouse labels: - {{- include "sentry.labels" . | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} type: Opaque stringData: # The official clickhouse-server image locks down *network* access @@ -42,22 +42,22 @@ stringData: # Not a substitute for task 2's per-tenant credentials (still unbuilt # -- see deploy/operator's Tenant controller); this is the shared # admin/migration credential only. - password: {{ include "sentry.stableSecretValue" (list .Release.Namespace (printf "%s-clickhouse" .Release.Name) "password" .Values.clickhouse.password) }} + password: {{ include "cairnobs.stableSecretValue" (list .Release.Namespace (printf "%s-clickhouse" .Release.Name) "password" .Values.clickhouse.password) }} --- apiVersion: v1 kind: Secret metadata: name: {{ .Release.Name }}-postgres labels: - {{- include "sentry.labels" . | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} type: Opaque stringData: - password: {{ include "sentry.stableSecretValue" (list .Release.Namespace (printf "%s-postgres" .Release.Name) "password" .Values.postgres.password) }} + password: {{ include "cairnobs.stableSecretValue" (list .Release.Namespace (printf "%s-postgres" .Release.Name) "password" .Values.postgres.password) }} # Restricted audit_writer Postgres role (Phase 4 task 4) -- INSERT+SELECT # only, via its own pool, never the shared role above. See # /docs/phase-4-isolation-design.md's audit-logging section and # metadata/README.md. - auditWriterPassword: {{ include "sentry.stableSecretValue" (list .Release.Namespace (printf "%s-postgres" .Release.Name) "auditWriterPassword" .Values.postgres.auditWriterPassword) }} + auditWriterPassword: {{ include "cairnobs.stableSecretValue" (list .Release.Namespace (printf "%s-postgres" .Release.Name) "auditWriterPassword" .Values.postgres.auditWriterPassword) }} {{- if .Values.enterprise.enabled }} --- apiVersion: v1 @@ -65,7 +65,7 @@ kind: Secret metadata: name: {{ .Release.Name }}-enterprise-auth labels: - {{- include "sentry.labels" . | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} type: Opaque stringData: # Must be >= 32 bytes -- see enterprise/internal/config.Load and @@ -74,7 +74,7 @@ stringData: # "don't rotate a live credential silently" reasoning as above, # which is why it's kept stable via the lookup above rather than # regenerated on every `helm upgrade`. - sessionSigningKey: {{ include "sentry.stableSecretValue" (list .Release.Namespace (printf "%s-enterprise-auth" .Release.Name) "sessionSigningKey" .Values.enterprise.sessionSigningKey) }} + sessionSigningKey: {{ include "cairnobs.stableSecretValue" (list .Release.Namespace (printf "%s-enterprise-auth" .Release.Name) "sessionSigningKey" .Values.enterprise.sessionSigningKey) }} {{- if .Values.enterprise.oidc.clientSecret }} oidcClientSecret: {{ .Values.enterprise.oidc.clientSecret | quote }} {{- end }} diff --git a/deploy/helm/sentry/templates/tenant-operator.yaml b/deploy/helm/cairnobs/templates/tenant-operator.yaml similarity index 82% rename from deploy/helm/sentry/templates/tenant-operator.yaml rename to deploy/helm/cairnobs/templates/tenant-operator.yaml index 31c077d..d3ed1cb 100644 --- a/deploy/helm/sentry/templates/tenant-operator.yaml +++ b/deploy/helm/cairnobs/templates/tenant-operator.yaml @@ -4,10 +4,10 @@ kind: ServiceAccount metadata: name: {{ .Release.Name }}-tenant-operator labels: - {{- include "sentry.labels" . | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} --- # ClusterRole, not Role: Tenant is cluster-scoped-CRD-but-namespaced-object -# (see crds/sentry.io_tenants.yaml's scope: Namespaced), and this chart +# (see crds/cairnobs.io_tenants.yaml's scope: Namespaced), and this chart # doesn't assume it's the only namespace the operator might one day watch # -- narrowed to exactly the two resource types # deploy/operator/internal/controller/tenant_controller.go's @@ -22,12 +22,12 @@ kind: ClusterRole metadata: name: {{ .Release.Name }}-tenant-operator labels: - {{- include "sentry.labels" . | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} rules: - - apiGroups: ["sentry.io"] + - apiGroups: ["cairnobs.io"] resources: ["tenants"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - - apiGroups: ["sentry.io"] + - apiGroups: ["cairnobs.io"] resources: ["tenants/status"] verbs: ["get", "update", "patch"] --- @@ -36,7 +36,7 @@ kind: ClusterRoleBinding metadata: name: {{ .Release.Name }}-tenant-operator labels: - {{- include "sentry.labels" . | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole @@ -51,8 +51,8 @@ kind: Deployment metadata: name: {{ .Release.Name }}-tenant-operator labels: - {{- include "sentry.labels" . | nindent 4 }} - {{- include "sentry.selectorLabels" (list $ "tenant-operator") | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "tenant-operator") | nindent 4 }} spec: # One replica -- see deploy/operator/cmd/tenant-operator/main.go's # comment: no leader election yet, a second replica could @@ -60,11 +60,11 @@ spec: replicas: 1 selector: matchLabels: - {{- include "sentry.selectorLabels" (list $ "tenant-operator") | nindent 6 }} + {{- include "cairnobs.selectorLabels" (list $ "tenant-operator") | nindent 6 }} template: metadata: labels: - {{- include "sentry.selectorLabels" (list $ "tenant-operator") | nindent 8 }} + {{- include "cairnobs.selectorLabels" (list $ "tenant-operator") | nindent 8 }} spec: serviceAccountName: {{ .Release.Name }}-tenant-operator containers: diff --git a/deploy/helm/sentry/templates/tenants.yaml b/deploy/helm/cairnobs/templates/tenants.yaml similarity index 75% rename from deploy/helm/sentry/templates/tenants.yaml rename to deploy/helm/cairnobs/templates/tenants.yaml index 9e782a4..dfcd793 100644 --- a/deploy/helm/sentry/templates/tenants.yaml +++ b/deploy/helm/cairnobs/templates/tenants.yaml @@ -1,12 +1,12 @@ {{- if .Values.enterprise.enabled }} {{- range .Values.tenants }} --- -apiVersion: sentry.io/v1alpha1 +apiVersion: cairnobs.io/v1alpha1 kind: Tenant metadata: name: {{ .name }} labels: - {{- include "sentry.labels" $ | nindent 4 }} + {{- include "cairnobs.labels" $ | nindent 4 }} spec: displayName: {{ .displayName | default .name | quote }} suspended: {{ .suspended | default false }} diff --git a/deploy/helm/sentry/templates/web.yaml b/deploy/helm/cairnobs/templates/web.yaml similarity index 66% rename from deploy/helm/sentry/templates/web.yaml rename to deploy/helm/cairnobs/templates/web.yaml index 397764f..0e9c3fc 100644 --- a/deploy/helm/sentry/templates/web.yaml +++ b/deploy/helm/cairnobs/templates/web.yaml @@ -3,17 +3,17 @@ kind: Deployment metadata: name: {{ .Release.Name }}-web labels: - {{- include "sentry.labels" . | nindent 4 }} - {{- include "sentry.selectorLabels" (list $ "web") | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "web") | nindent 4 }} spec: replicas: {{ .Values.web.replicas }} selector: matchLabels: - {{- include "sentry.selectorLabels" (list $ "web") | nindent 6 }} + {{- include "cairnobs.selectorLabels" (list $ "web") | nindent 6 }} template: metadata: labels: - {{- include "sentry.selectorLabels" (list $ "web") | nindent 8 }} + {{- include "cairnobs.selectorLabels" (list $ "web") | nindent 8 }} spec: containers: - name: web @@ -33,11 +33,11 @@ kind: Service metadata: name: {{ .Release.Name }}-web labels: - {{- include "sentry.labels" . | nindent 4 }} - {{- include "sentry.selectorLabels" (list $ "web") | nindent 4 }} + {{- include "cairnobs.labels" . | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "web") | nindent 4 }} spec: selector: - {{- include "sentry.selectorLabels" (list $ "web") | nindent 4 }} + {{- include "cairnobs.selectorLabels" (list $ "web") | nindent 4 }} ports: - name: http port: 3000 diff --git a/deploy/helm/sentry/values.yaml b/deploy/helm/cairnobs/values.yaml similarity index 92% rename from deploy/helm/sentry/values.yaml rename to deploy/helm/cairnobs/values.yaml index b867464..2915df5 100644 --- a/deploy/helm/sentry/values.yaml +++ b/deploy/helm/cairnobs/values.yaml @@ -1,9 +1,9 @@ -# Default values for the sentry chart. See deploy/helm/sentry/README.md +# Default values for the cairnobs chart. See deploy/helm/cairnobs/README.md # for the multi-tenant-specific values (enterprise.*, tenants) and what # "multi-tenant-aware" does and doesn't mean at this layer. # # Image repositories default to locally-built tags matching each -# service's docker-compose.yml container_name, minus the "sentry-" +# service's docker-compose.yml container_name, minus the "cairnobs-" # container_name prefix duplication -- push these to a registry this # cluster can actually pull from before installing; this chart never # builds images itself (same division of labor as docker-compose.yml: @@ -22,7 +22,7 @@ redpanda: # Built from ./transport (docker-compose.yml's redpanda-provision # service) -- the one-shot topic-creation Job below. provisionImage: - repository: sentry-redpanda-provision + repository: cairnobs-redpanda-provision tag: latest clickhouse: @@ -38,7 +38,7 @@ clickhouse: # Built from ./storage (docker-compose.yml's clickhouse-migrate # service) -- the one-shot schema-migration Job. migrateImage: - repository: sentry-clickhouse-migrate + repository: cairnobs-clickhouse-migrate tag: latest postgres: @@ -53,12 +53,12 @@ postgres: # Built from ./metadata (docker-compose.yml's metadata-migrate # service) -- the one-shot schema-migration Job. migrateImage: - repository: sentry-metadata-migrate + repository: cairnobs-metadata-migrate tag: latest ingest: image: - repository: sentry-ingest + repository: cairnobs-ingest tag: latest replicas: 1 resources: {} @@ -87,7 +87,7 @@ ingest: search: image: - repository: sentry-search + repository: cairnobs-search tag: latest # Pinned to 1: search consumes the same Redpanda topic ingest's # consumer does with its own offset tracking (see search/README.md). @@ -103,14 +103,14 @@ search: api: image: - repository: sentry-api + repository: cairnobs-api tag: latest replicas: 2 resources: {} alerting: image: - repository: sentry-alerting + repository: cairnobs-alerting tag: latest # Pinned to 1 for the same reason as search: rulestore.ClaimDueRules # has no leader-election/partitioning story for multiple evaluator @@ -125,7 +125,7 @@ alerting: web: image: - repository: sentry-web + repository: cairnobs-web tag: latest replicas: 2 resources: {} @@ -151,7 +151,7 @@ web: enterprise: enabled: false image: - repository: sentry-enterprise-auth + repository: cairnobs-enterprise-auth tag: latest # enterprise-api (templates/enterprise-api.yaml) -- swaps in for # api.yaml's plain api Deployment when enterprise.enabled is true, on @@ -160,7 +160,7 @@ enterprise: # enterprise-auth's image above -- see # enterprise/cmd/enterprise-api/Dockerfile. apiImage: - repository: sentry-enterprise-api + repository: cairnobs-enterprise-api tag: latest # enterprise-ingest (templates/enterprise-ingest.yaml) -- only # rendered when ingest.requireTenantCredential is also true (see that @@ -169,7 +169,7 @@ enterprise: # "repo root build context" reasoning as apiImage above -- see # enterprise/cmd/enterprise-ingest/Dockerfile. ingestImage: - repository: sentry-enterprise-ingest + repository: cairnobs-enterprise-ingest tag: latest replicas: 1 resources: {} @@ -195,7 +195,7 @@ enterprise: tenantOperator: enabled: false image: - repository: sentry-tenant-operator + repository: cairnobs-tenant-operator tag: latest resources: {} diff --git a/deploy/operator/Dockerfile b/deploy/operator/Dockerfile index eb96f02..8332499 100644 --- a/deploy/operator/Dockerfile +++ b/deploy/operator/Dockerfile @@ -1,7 +1,7 @@ # Same shape as every other Go service's Dockerfile in this repo # (alerting/Dockerfile, enterprise/Dockerfile) -- context is # deploy/operator/ itself, no /proto dependency. -# docker build -f deploy/operator/Dockerfile -t sentry-tenant-operator deploy/operator/ +# docker build -f deploy/operator/Dockerfile -t cairnobs-tenant-operator deploy/operator/ FROM golang:1.25-alpine AS builder WORKDIR /src COPY . . diff --git a/deploy/operator/README.md b/deploy/operator/README.md index c078ee6..62473ec 100644 --- a/deploy/operator/README.md +++ b/deploy/operator/README.md @@ -1,7 +1,7 @@ # deploy/operator A small `controller-runtime` Operator managing one CRD: `Tenant` -(`sentry.io/v1alpha1`). See `internal/controller/tenant_controller.go`'s +(`cairnobs.io/v1alpha1`). See `internal/controller/tenant_controller.go`'s doc comment for exactly what it reconciles and -- just as importantly -- what it deliberately doesn't (no ClickHouse calls, no Tantivy filesystem access, no `enterprise/internal/rbacstore` wiring; those are @@ -17,7 +17,7 @@ environment, so this package is hand-written rather than generated: `api/v1alpha1/api_test.go`'s round-trip tests (mutate a copy, assert the original is untouched -- exactly the class of bug a hand-written `DeepCopy` is prone to). -- `config/crd/sentry.io_tenants.yaml` -- normally `controller-gen crd` +- `config/crd/cairnobs.io_tenants.yaml` -- normally `controller-gen crd` output from the `+kubebuilder:validation:*` markers on `api/v1alpha1/tenant_types.go`; hand-written here and only as strong as keeping the two in sync by hand. Validated by strict-unmarshaling it @@ -29,7 +29,7 @@ environment, so this package is hand-written rather than generated: are present as documentation/intent (matching kubebuilder convention) but were never run through `controller-gen rbac` -- the actual ClusterRole is hand-written in - `/deploy/helm/sentry/templates/tenant-operator.yaml`, kept in sync with + `/deploy/helm/cairnobs/templates/tenant-operator.yaml`, kept in sync with those markers by hand, same caveat as the CRD above. ## Layout @@ -59,7 +59,7 @@ garbage collection, watch-triggered re-reconciliation) -- see `internal/controller/tenant_controller_test.go`'s doc comment. ```sh -docker build -f Dockerfile -t sentry-tenant-operator . # context is deploy/operator/, not the repo root +docker build -f Dockerfile -t cairnobs-tenant-operator . # context is deploy/operator/, not the repo root ``` Not verified in this session -- see `/deploy/README.md`. @@ -67,9 +67,9 @@ Not verified in this session -- see `/deploy/README.md`. ## Trying it against a real cluster ```sh -kubectl apply -f config/crd/sentry.io_tenants.yaml +kubectl apply -f config/crd/cairnobs.io_tenants.yaml kubectl apply -f - <<'EOF' -apiVersion: sentry.io/v1alpha1 +apiVersion: cairnobs.io/v1alpha1 kind: Tenant metadata: name: acme @@ -77,5 +77,5 @@ spec: displayName: "Acme Corp" EOF kubectl get tenant acme -o yaml # status.phase should reach Active -kubectl get secret sentry-tenant-acme-clickhouse -o yaml +kubectl get secret cairnobs-tenant-acme-clickhouse -o yaml ``` diff --git a/deploy/operator/api/v1alpha1/groupversion_info.go b/deploy/operator/api/v1alpha1/groupversion_info.go index 4f77b07..989fa93 100644 --- a/deploy/operator/api/v1alpha1/groupversion_info.go +++ b/deploy/operator/api/v1alpha1/groupversion_info.go @@ -7,7 +7,7 @@ // cluster). // // +kubebuilder:object:generate=true -// +groupName=sentry.io +// +groupName=cairnobs.io package v1alpha1 import ( @@ -16,8 +16,8 @@ import ( ) var ( - // GroupVersion is group sentry.io, version v1alpha1. - GroupVersion = schema.GroupVersion{Group: "sentry.io", Version: "v1alpha1"} + // GroupVersion is group cairnobs.io, version v1alpha1. + GroupVersion = schema.GroupVersion{Group: "cairnobs.io", Version: "v1alpha1"} // SchemeBuilder is used to add go types to the GroupVersionKind scheme. SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} diff --git a/deploy/operator/api/v1alpha1/tenant_types.go b/deploy/operator/api/v1alpha1/tenant_types.go index b005f14..10cc814 100644 --- a/deploy/operator/api/v1alpha1/tenant_types.go +++ b/deploy/operator/api/v1alpha1/tenant_types.go @@ -117,7 +117,7 @@ const ConditionReady = "Ready" // +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=`.status.phase` // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` -// Tenant is the K8s-native representation of one Sentry tenant's +// Tenant is the K8s-native representation of one Cairn OBS tenant's // deployment-topology state -- see this file's package-level doc // comment for what it does and does not manage today. type Tenant struct { diff --git a/deploy/operator/cmd/tenant-operator/main.go b/deploy/operator/cmd/tenant-operator/main.go index 795c270..ad14b5d 100644 --- a/deploy/operator/cmd/tenant-operator/main.go +++ b/deploy/operator/cmd/tenant-operator/main.go @@ -18,15 +18,15 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/metrics/server" - sentryv1alpha1 "github.com/sentry/sentry/deploy/operator/api/v1alpha1" - "github.com/sentry/sentry/deploy/operator/internal/controller" + cairnobsv1alpha1 "github.com/cairnobs/cairnobs/deploy/operator/api/v1alpha1" + "github.com/cairnobs/cairnobs/deploy/operator/internal/controller" ) var scheme = runtime.NewScheme() func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - utilruntime.Must(sentryv1alpha1.AddToScheme(scheme)) + utilruntime.Must(cairnobsv1alpha1.AddToScheme(scheme)) } func main() { diff --git a/deploy/operator/config/crd/sentry.io_tenants.yaml b/deploy/operator/config/crd/cairnobs.io_tenants.yaml similarity index 98% rename from deploy/operator/config/crd/sentry.io_tenants.yaml rename to deploy/operator/config/crd/cairnobs.io_tenants.yaml index 5120cf0..578eaa3 100644 --- a/deploy/operator/config/crd/sentry.io_tenants.yaml +++ b/deploy/operator/config/crd/cairnobs.io_tenants.yaml @@ -7,9 +7,9 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: - name: tenants.sentry.io + name: tenants.cairnobs.io spec: - group: sentry.io + group: cairnobs.io names: kind: Tenant listKind: TenantList diff --git a/deploy/operator/go.mod b/deploy/operator/go.mod index a7514d3..9f65266 100644 --- a/deploy/operator/go.mod +++ b/deploy/operator/go.mod @@ -1,4 +1,4 @@ -module github.com/sentry/sentry/deploy/operator +module github.com/cairnobs/cairnobs/deploy/operator go 1.25.0 diff --git a/deploy/operator/internal/controller/tenant_controller.go b/deploy/operator/internal/controller/tenant_controller.go index 4661500..0459cf7 100644 --- a/deploy/operator/internal/controller/tenant_controller.go +++ b/deploy/operator/internal/controller/tenant_controller.go @@ -35,7 +35,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - sentryv1alpha1 "github.com/sentry/sentry/deploy/operator/api/v1alpha1" + cairnobsv1alpha1 "github.com/cairnobs/cairnobs/deploy/operator/api/v1alpha1" ) // TenantReconciler reconciles a Tenant object. @@ -44,11 +44,11 @@ type TenantReconciler struct { Scheme *runtime.Scheme } -// +kubebuilder:rbac:groups=sentry.io,resources=tenants,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=sentry.io,resources=tenants/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=cairnobs.io,resources=tenants,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=cairnobs.io,resources=tenants/status,verbs=get;update;patch func (r *TenantReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - var tenant sentryv1alpha1.Tenant + var tenant cairnobsv1alpha1.Tenant if err := r.Get(ctx, req.NamespacedName, &tenant); err != nil { if apierrors.IsNotFound(err) { // Deleted -- the owned Secret -provision-tenant created (if @@ -74,19 +74,19 @@ func (r *TenantReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr reason, message := "AwaitingProvisioning", "waiting for enterprise-api -provision-tenant to provision ClickHouse for this tenant" switch { case tenant.Spec.Suspended: - tenant.Status.Phase = sentryv1alpha1.PhaseSuspended + tenant.Status.Phase = cairnobsv1alpha1.PhaseSuspended reason, message = "Suspended", "tenant is suspended (spec.suspended=true)" case provisioned: - tenant.Status.Phase = sentryv1alpha1.PhaseActive + tenant.Status.Phase = cairnobsv1alpha1.PhaseActive condStatus = metav1.ConditionTrue reason, message = "Provisioned", fmt.Sprintf("ClickHouse database %q is provisioned", tenant.Status.ClickHouseDatabaseName) default: - tenant.Status.Phase = sentryv1alpha1.PhaseProvisioning + tenant.Status.Phase = cairnobsv1alpha1.PhaseProvisioning } tenant.Status.ObservedGeneration = tenant.Generation meta.SetStatusCondition(&tenant.Status.Conditions, metav1.Condition{ - Type: sentryv1alpha1.ConditionReady, + Type: cairnobsv1alpha1.ConditionReady, Status: condStatus, Reason: reason, Message: message, @@ -102,6 +102,6 @@ func (r *TenantReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr func (r *TenantReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&sentryv1alpha1.Tenant{}). + For(&cairnobsv1alpha1.Tenant{}). Complete(r) } diff --git a/deploy/operator/internal/controller/tenant_controller_test.go b/deploy/operator/internal/controller/tenant_controller_test.go index 688ab4d..63420e9 100644 --- a/deploy/operator/internal/controller/tenant_controller_test.go +++ b/deploy/operator/internal/controller/tenant_controller_test.go @@ -23,7 +23,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" - sentryv1alpha1 "github.com/sentry/sentry/deploy/operator/api/v1alpha1" + cairnobsv1alpha1 "github.com/cairnobs/cairnobs/deploy/operator/api/v1alpha1" ) func newFakeReconciler(t *testing.T, objs ...client.Object) *TenantReconciler { @@ -32,31 +32,31 @@ func newFakeReconciler(t *testing.T, objs ...client.Object) *TenantReconciler { if err := corev1.AddToScheme(scheme); err != nil { t.Fatalf("adding corev1 to scheme: %v", err) } - if err := sentryv1alpha1.AddToScheme(scheme); err != nil { - t.Fatalf("adding sentryv1alpha1 to scheme: %v", err) + if err := cairnobsv1alpha1.AddToScheme(scheme); err != nil { + t.Fatalf("adding cairnobsv1alpha1 to scheme: %v", err) } fakeClient := fake.NewClientBuilder(). WithScheme(scheme). WithObjects(objs...). - WithStatusSubresource(&sentryv1alpha1.Tenant{}). + WithStatusSubresource(&cairnobsv1alpha1.Tenant{}). Build() return &TenantReconciler{Client: fakeClient, Scheme: scheme} } -func testTenant(name string, suspended bool) *sentryv1alpha1.Tenant { - return &sentryv1alpha1.Tenant{ +func testTenant(name string, suspended bool) *cairnobsv1alpha1.Tenant { + return &cairnobsv1alpha1.Tenant{ ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: "default"}, - Spec: sentryv1alpha1.TenantSpec{DisplayName: name, Suspended: suspended}, + Spec: cairnobsv1alpha1.TenantSpec{DisplayName: name, Suspended: suspended}, } } -func reconcile(t *testing.T, r *TenantReconciler, name string) sentryv1alpha1.Tenant { +func reconcile(t *testing.T, r *TenantReconciler, name string) cairnobsv1alpha1.Tenant { t.Helper() ctx := context.Background() if _, err := r.Reconcile(ctx, ctrl.Request{NamespacedName: types.NamespacedName{Name: name, Namespace: "default"}}); err != nil { t.Fatalf("Reconcile: %v", err) } - var got sentryv1alpha1.Tenant + var got cairnobsv1alpha1.Tenant if err := r.Get(ctx, types.NamespacedName{Name: name, Namespace: "default"}, &got); err != nil { t.Fatalf("getting tenant: %v", err) } @@ -74,7 +74,7 @@ func TestReconcileUnprovisionedTenantIsProvisioningNotActive(t *testing.T) { got := reconcile(t, r, "acme") - if got.Status.Phase != sentryv1alpha1.PhaseProvisioning { + if got.Status.Phase != cairnobsv1alpha1.PhaseProvisioning { t.Fatalf("Phase = %q, want Provisioning (nothing has provisioned this tenant yet)", got.Status.Phase) } cond := readyCondition(got) @@ -93,23 +93,23 @@ func TestReconcileReflectsProvisioningStateProvisionTenantSets(t *testing.T) { r := newFakeReconciler(t, tenant) ctx := context.Background() - var toUpdate sentryv1alpha1.Tenant + var toUpdate cairnobsv1alpha1.Tenant if err := r.Get(ctx, types.NamespacedName{Name: "acme", Namespace: "default"}, &toUpdate); err != nil { t.Fatalf("getting tenant: %v", err) } toUpdate.Status.ClickHouseDatabaseName = "acme" - toUpdate.Status.ClickHouseSecretRef = "sentry-tenant-acme-clickhouse" - toUpdate.Status.TantivyIndexPath = "/var/lib/sentry-search/tenants/acme" + toUpdate.Status.ClickHouseSecretRef = "cairnobs-tenant-acme-clickhouse" + toUpdate.Status.TantivyIndexPath = "/var/lib/cairnobs-search/tenants/acme" if err := r.Status().Update(ctx, &toUpdate); err != nil { t.Fatalf("simulating -provision-tenant's status write: %v", err) } got := reconcile(t, r, "acme") - if got.Status.Phase != sentryv1alpha1.PhaseActive { + if got.Status.Phase != cairnobsv1alpha1.PhaseActive { t.Fatalf("Phase = %q, want Active", got.Status.Phase) } - if got.Status.ClickHouseDatabaseName != "acme" || got.Status.ClickHouseSecretRef != "sentry-tenant-acme-clickhouse" || got.Status.TantivyIndexPath != "/var/lib/sentry-search/tenants/acme" { + if got.Status.ClickHouseDatabaseName != "acme" || got.Status.ClickHouseSecretRef != "cairnobs-tenant-acme-clickhouse" || got.Status.TantivyIndexPath != "/var/lib/cairnobs-search/tenants/acme" { t.Fatalf("reconcile must not clobber the fields -provision-tenant set: %+v", got.Status) } cond := readyCondition(got) @@ -125,7 +125,7 @@ func TestReconcileSuspendedOverridesProvisionedState(t *testing.T) { got := reconcile(t, r, "acme") - if got.Status.Phase != sentryv1alpha1.PhaseSuspended { + if got.Status.Phase != cairnobsv1alpha1.PhaseSuspended { t.Fatalf("Phase = %q, want Suspended even though the tenant is provisioned", got.Status.Phase) } } @@ -144,7 +144,7 @@ func TestReconcileUnsuspendingReturnsToActiveNotProvisioning(t *testing.T) { _ = reconcile(t, r, "acme") // establishes Suspended - var toUpdate sentryv1alpha1.Tenant + var toUpdate cairnobsv1alpha1.Tenant if err := r.Get(ctx, types.NamespacedName{Name: "acme", Namespace: "default"}, &toUpdate); err != nil { t.Fatalf("getting tenant: %v", err) } @@ -154,7 +154,7 @@ func TestReconcileUnsuspendingReturnsToActiveNotProvisioning(t *testing.T) { } got := reconcile(t, r, "acme") - if got.Status.Phase != sentryv1alpha1.PhaseActive { + if got.Status.Phase != cairnobsv1alpha1.PhaseActive { t.Fatalf("Phase = %q, want Active after unsuspending an already-provisioned tenant", got.Status.Phase) } } @@ -167,9 +167,9 @@ func TestReconcileMissingTenantIsNoOp(t *testing.T) { } } -func readyCondition(tenant sentryv1alpha1.Tenant) *metav1.Condition { +func readyCondition(tenant cairnobsv1alpha1.Tenant) *metav1.Condition { for i := range tenant.Status.Conditions { - if tenant.Status.Conditions[i].Type == sentryv1alpha1.ConditionReady { + if tenant.Status.Conditions[i].Type == cairnobsv1alpha1.ConditionReady { return &tenant.Status.Conditions[i] } } diff --git a/docker-compose.yml b/docker-compose.yml index 91293ba..0066aff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,7 +16,7 @@ services: redpanda: image: docker.redpanda.com/redpandadata/redpanda:v24.2.7 - container_name: sentry-redpanda + container_name: cairnobs-redpanda command: - redpanda - start @@ -43,7 +43,7 @@ services: redpanda-provision: build: context: ./transport - container_name: sentry-redpanda-provision + container_name: cairnobs-redpanda-provision depends_on: redpanda: condition: service_healthy @@ -57,7 +57,7 @@ services: clickhouse: image: clickhouse/clickhouse-server:24.8 - container_name: sentry-clickhouse + container_name: cairnobs-clickhouse ports: - "8123:8123" # HTTP interface, used by the migrate step - "9000:9000" # native protocol, used by ingest and api @@ -71,7 +71,7 @@ services: # password isn't a real secret (mTLS between agent and ingest is # the actual security boundary here) — it exists purely to satisfy # this image's login gate for local/homelab use. - CLICKHOUSE_PASSWORD: "sentry-dev-only" + CLICKHOUSE_PASSWORD: "cairnobs-dev-only" # Phase 4's per-tenant provisioning (enterprise/internal/tenantprovision) # runs CREATE USER/GRANT against this connection as the ClickHouse # admin -- the official image's default user doesn't have @@ -104,13 +104,13 @@ services: clickhouse-migrate: build: context: ./storage - container_name: sentry-clickhouse-migrate + container_name: cairnobs-clickhouse-migrate depends_on: clickhouse: condition: service_healthy environment: CLICKHOUSE_HTTP: "http://clickhouse:8123" - CLICKHOUSE_PASSWORD: "sentry-dev-only" + CLICKHOUSE_PASSWORD: "cairnobs-dev-only" # Control-plane metadata store (dashboards, alert rules -- see # /docs/phase-3-dashboard-design.md for why this is Postgres rather @@ -118,11 +118,11 @@ services: # only, unaffected. metadata-postgres: image: postgres:16-alpine - container_name: sentry-metadata-postgres + container_name: cairnobs-metadata-postgres environment: POSTGRES_DB: sentry_metadata POSTGRES_USER: sentry - POSTGRES_PASSWORD: "sentry-dev-only" # not a real secret, same framing as CLICKHOUSE_PASSWORD above + POSTGRES_PASSWORD: "cairnobs-dev-only" # not a real secret, same framing as CLICKHOUSE_PASSWORD above volumes: - metadata-postgres-data:/var/lib/postgresql/data healthcheck: @@ -136,7 +136,7 @@ services: metadata-migrate: build: context: ./metadata - container_name: sentry-metadata-migrate + container_name: cairnobs-metadata-migrate depends_on: metadata-postgres: condition: service_healthy @@ -144,7 +144,7 @@ services: POSTGRES_HOST: "metadata-postgres" POSTGRES_PORT: "5432" POSTGRES_USER: "sentry" - POSTGRES_PASSWORD: "sentry-dev-only" + POSTGRES_PASSWORD: "cairnobs-dev-only" POSTGRES_DATABASE: "sentry_metadata" # Password for the restricted audit_writer Postgres role (Phase 4 # task 4) -- INSERT+SELECT only on audit_log, never UPDATE/DELETE, @@ -156,7 +156,7 @@ services: build: context: . # needs both ingest/ and proto/ dockerfile: ingest/Dockerfile - container_name: sentry-ingest + container_name: cairnobs-ingest depends_on: redpanda-provision: condition: service_completed_successfully @@ -169,9 +169,9 @@ services: environment: REDPANDA_BROKERS: "redpanda:9092" CLICKHOUSE_ADDR: "clickhouse:9000" - CLICKHOUSE_PASSWORD: "sentry-dev-only" + CLICKHOUSE_PASSWORD: "cairnobs-dev-only" # TLS_*_FILE env vars are left at their defaults - # (/etc/sentry-ingest/{server,server-key,ca}.pem) — matches where + # (/etc/cairnobs-ingest/{server,server-key,ca}.pem) — matches where # the volume below mounts the generated dev certs. # # ENTERPRISE_AUTH_URL is deliberately NOT set here (see @@ -193,9 +193,9 @@ services: # tenancy prerequisite -- it works the same in single-tenant core. AGENT_REGISTRY_POSTGRES_ADDR: "metadata-postgres:5432" AGENT_REGISTRY_POSTGRES_USERNAME: "sentry" - AGENT_REGISTRY_POSTGRES_PASSWORD: "sentry-dev-only" + AGENT_REGISTRY_POSTGRES_PASSWORD: "cairnobs-dev-only" volumes: - - ./hack/dev-certs/out:/etc/sentry-ingest:ro + - ./hack/dev-certs/out:/etc/cairnobs-ingest:ro # Reads the same sentry.logs.raw topic ingest's consumer does (own # offset tracking, own failure domain — see /search/README.md) and @@ -204,7 +204,7 @@ services: build: context: . # needs both search/ and proto/ dockerfile: search/Dockerfile - container_name: sentry-search + container_name: cairnobs-search depends_on: redpanda-provision: condition: service_completed_successfully @@ -226,10 +226,10 @@ services: # before this tracker existed -- see search/README.md's "Per-tenant # indices" section for how to turn it on for manual testing. volumes: - - search-index-data:/var/lib/sentry-search + - search-index-data:/var/lib/cairnobs-search # Mutually exclusive with enterprise-api below, same choice Helm makes - # via enterprise.enabled (deploy/helm/sentry/templates/api.yaml vs + # via enterprise.enabled (deploy/helm/cairnobs/templates/api.yaml vs # enterprise-api.yaml) -- selected by the COMPOSE_PROFILES value in # .env (checked in as "single-tenant", the zero-config default) or an # override on the command line, e.g. `COMPOSE_PROFILES=enterprise @@ -242,7 +242,7 @@ services: build: context: . # needs both api/ and proto/ (gRPC client to search) dockerfile: api/Dockerfile - container_name: sentry-api + container_name: cairnobs-api depends_on: clickhouse-migrate: condition: service_completed_successfully @@ -252,12 +252,12 @@ services: - "8080:8080" environment: CLICKHOUSE_ADDR: "clickhouse:9000" - CLICKHOUSE_PASSWORD: "sentry-dev-only" + CLICKHOUSE_PASSWORD: "cairnobs-dev-only" SEARCH_GRPC_ADDR: "search:50052" POSTGRES_ADDR: "metadata-postgres:5432" POSTGRES_DATABASE: "sentry_metadata" POSTGRES_USERNAME: "sentry" - POSTGRES_PASSWORD: "sentry-dev-only" + POSTGRES_PASSWORD: "cairnobs-dev-only" healthcheck: # alerting (Phase 3 task 5) depends_on api -- without this, that # dependency can only mean "container started," not "actually @@ -274,7 +274,7 @@ services: build: context: alerting # self-contained, no /proto needed -- see alerting/Dockerfile dockerfile: Dockerfile - container_name: sentry-alerting + container_name: cairnobs-alerting depends_on: metadata-migrate: condition: service_completed_successfully @@ -295,7 +295,7 @@ services: POSTGRES_ADDR: "metadata-postgres:5432" POSTGRES_DATABASE: "sentry_metadata" POSTGRES_USERNAME: "sentry" - POSTGRES_PASSWORD: "sentry-dev-only" + POSTGRES_PASSWORD: "cairnobs-dev-only" # Resolves to whichever of api/enterprise-api is actually active -- # enterprise-api declares a `default.aliases: [api]` network alias # below specifically so this never needs to change based on which @@ -319,14 +319,14 @@ services: # no admin UI to create the first tenant_memberships row -- see # /docs/phase-4-runbook.md sections 3a/3b for wiring a real IdP and # bootstrapping that row by hand. Flipping enforcement on by default - # without that would break the web UI and sentryctl with no way to log + # without that would break the web UI and cairnobsctl with no way to log # in. See enterprise/README.md for how to turn enforcement on for # manual testing (mint a service token, set the two env vars, restart). enterprise-auth: build: context: . # needs api/, ingest/, proto/, and enterprise/ itself -- see enterprise/Dockerfile's doc comment dockerfile: enterprise/Dockerfile - container_name: sentry-enterprise-auth + container_name: cairnobs-enterprise-auth depends_on: metadata-migrate: condition: service_completed_successfully @@ -335,11 +335,11 @@ services: environment: # Dev-only, same framing as CLICKHOUSE_PASSWORD above -- not a real # secret. Must be at least 32 bytes (see internal/config.Load). - ENTERPRISE_SESSION_SIGNING_KEY: "sentry-dev-only-session-signing-key-32bytes+" + ENTERPRISE_SESSION_SIGNING_KEY: "cairnobs-dev-only-session-signing-key-32bytes+" POSTGRES_ADDR: "metadata-postgres:5432" POSTGRES_DATABASE: "sentry_metadata" POSTGRES_USERNAME: "sentry" - POSTGRES_PASSWORD: "sentry-dev-only" + POSTGRES_PASSWORD: "cairnobs-dev-only" # Where the browser lands after internal/loginhandler sets a # session cookie -- web's mapped host port (see web's build args # for why this is localhost:3000, not the compose network's @@ -372,7 +372,7 @@ services: build: context: . dockerfile: enterprise/cmd/enterprise-api/Dockerfile - container_name: sentry-enterprise-api + container_name: cairnobs-enterprise-api depends_on: clickhouse-migrate: condition: service_completed_successfully @@ -388,12 +388,12 @@ services: HTTP_LISTEN_ADDR: ":8080" CLICKHOUSE_ADDR: "clickhouse:9000" CLICKHOUSE_ADMIN_USERNAME: "default" - CLICKHOUSE_ADMIN_PASSWORD: "sentry-dev-only" + CLICKHOUSE_ADMIN_PASSWORD: "cairnobs-dev-only" SEARCH_GRPC_ADDR: "search:50052" POSTGRES_ADDR: "metadata-postgres:5432" POSTGRES_DATABASE: "sentry_metadata" POSTGRES_USERNAME: "sentry" - POSTGRES_PASSWORD: "sentry-dev-only" + POSTGRES_PASSWORD: "cairnobs-dev-only" AUDIT_WRITER_USERNAME: "audit_writer" AUDIT_WRITER_PASSWORD: "audit-writer-dev-only" ENTERPRISE_AUTH_URL: "http://enterprise-auth:8082" @@ -415,14 +415,14 @@ services: # groups) and write it -- ingest into the one shared `logs` table, # this into each tenant's own database. Harmless duplication for local # testing/verification purposes, not what a real deployment does (see - # deploy/helm/sentry's ingest.yaml/enterprise-ingest.yaml, which + # deploy/helm/cairnobs's ingest.yaml/enterprise-ingest.yaml, which # actually achieve exclusivity via -mode=server/-mode=consumer). enterprise-ingest: profiles: ["enterprise"] build: context: . dockerfile: enterprise/cmd/enterprise-ingest/Dockerfile - container_name: sentry-enterprise-ingest + container_name: cairnobs-enterprise-ingest depends_on: redpanda-provision: condition: service_completed_successfully @@ -436,7 +436,7 @@ services: POSTGRES_ADDR: "metadata-postgres:5432" POSTGRES_DATABASE: "sentry_metadata" POSTGRES_USERNAME: "sentry" - POSTGRES_PASSWORD: "sentry-dev-only" + POSTGRES_PASSWORD: "cairnobs-dev-only" healthcheck: test: ["CMD", "/enterprise-ingest", "-healthcheck"] interval: 5s @@ -457,7 +457,7 @@ services: VITE_API_BASE_URL: "http://localhost:8080" VITE_ALERTING_API_BASE_URL: "http://localhost:8081" VITE_ENTERPRISE_AUTH_BASE_URL: "http://localhost:8082" - container_name: sentry-web + container_name: cairnobs-web depends_on: # api/enterprise-api optional, same reasoning as alerting's # depends_on above -- only one is ever in the active profile set. diff --git a/docs/agent-heartbeat-monitoring.md b/docs/agent-heartbeat-monitoring.md index e682f3b..5552ea9 100644 --- a/docs/agent-heartbeat-monitoring.md +++ b/docs/agent-heartbeat-monitoring.md @@ -10,7 +10,7 @@ reachable, and how quickly the platform notices when it stops. The agent's transport has always been push-only, by design — it dials *out* to `ingest` over mTLS; nothing in the platform ever dials into an -agent (see `agent/sentry-agent/src/grpc.rs`'s doc comment). Making +agent (see `agent/cairnobs-agent/src/grpc.rs`'s doc comment). Making liveness detection a true pull (the platform reaching into every remote host on a schedule) would mean every agent needs a reachable address and an open inbound port — a real problem for hosts behind NAT or with @@ -28,7 +28,7 @@ model this reuses unchanged. ## Configuring the heartbeat -`agent/sentry-agent/config/agent.example.toml`: +`agent/cairnobs-agent/config/agent.example.toml`: ```toml [heartbeat] @@ -40,7 +40,7 @@ The heartbeat record is sent through the exact same `PushBatch` RPC and mTLS identity every log line uses, bypassing the batch buffer (`[batch]` `max_size`/`flush_interval_ms`) so it's punctual rather than subject to batching delay. It's distinguished from real log data purely by an -attribute — `sentry.heartbeat=true` — not by a fake `service` value, so +attribute — `cairnobs.heartbeat=true` — not by a fake `service` value, so it never pollutes service-based dashboards or faceting. `message` is the literal string `"agent heartbeat"`. @@ -55,7 +55,7 @@ heartbeat doesn't look like a false absence): curl -X POST http://localhost:8081/rules -H 'Content-Type: application/json' -d '{ "name": "web-01 unavailable", "description": "fires when web-01 misses its heartbeat window", - "query": "earliest=-3m host=web-01 sentry.heartbeat=true", + "query": "earliest=-3m host=web-01 cairnobs.heartbeat=true", "query_language": "spl", "condition_type": "absence", "eval_interval_seconds": 60, @@ -123,7 +123,7 @@ this was simply never exercised in this specific way before) and a curl -X POST http://localhost:8081/rules -H "Content-Type: application/json" -d "{ \"name\": \"fleet degraded\", \"description\": \"fires when fewer than 3 of the expected fleet hosts have heartbeated recently\", - \"query\": \"SELECT count(DISTINCT host) AS active_agents FROM logs WHERE timestamp > now() - INTERVAL 3 MINUTE AND attributes['sentry.heartbeat'] = 'true' AND host LIKE 'web-%'\", + \"query\": \"SELECT count(DISTINCT host) AS active_agents FROM logs WHERE timestamp > now() - INTERVAL 3 MINUTE AND attributes['cairnobs.heartbeat'] = 'true' AND host LIKE 'web-%'\", \"query_language\": \"sql\", \"condition_type\": \"threshold\", \"comparator\": \"lt\", @@ -172,8 +172,8 @@ wanted, not duplicated as a one-off script now. ## Verified live This exact flow was run end-to-end against a live stack in this repo: a -real `sentry-agent` binary, heartbeat interval 5s, connected to a real -`ingest`; an absence rule (`earliest=-45s host=... sentry.heartbeat=true`, +real `cairnobs-agent` binary, heartbeat interval 5s, connected to a real +`ingest`; an absence rule (`earliest=-45s host=... cairnobs.heartbeat=true`, `eval_interval_seconds=30`, `for_minutes=0`) created via the REST API above; the agent process killed; the rule transitioned `ok` → `firing` within one evaluation cycle (`condition_true_since`/`fired_at` both set diff --git a/docs/agent-management-design.md b/docs/agent-management-design.md index 974cc6b..f8ea8f1 100644 --- a/docs/agent-management-design.md +++ b/docs/agent-management-design.md @@ -44,7 +44,7 @@ New proto (`proto/sentry/agent/v1/agent_control.proto`), a second gRPC service on the exact same mTLS channel/listener `LogIngest.PushBatch` already uses — not a second protocol or connection the agent has to maintain. Called on the agent's own heartbeat ticker (see -`agent/sentry-agent/src/main.rs`'s `heartbeat_ticker` arm), +`agent/cairnobs-agent/src/main.rs`'s `heartbeat_ticker` arm), independently of whether the heartbeat log record itself is enabled — CheckIn keeps running even with `heartbeat.enabled = false`, since that's an agent's only path to ever receive a remote override that @@ -98,7 +98,7 @@ heartbeat constantly. The agent's local `agent.toml` is never rewritten. A remote override lives only in the running process's memory -(`agent/sentry-agent/src/main.rs`'s `apply_override`) and is re-applied +(`agent/cairnobs-agent/src/main.rs`'s `apply_override`) and is re-applied fresh on every check-in that returns one — a restarted agent boots from `agent.toml` alone and re-syncs whatever override is still set on its next successful check-in. This was a deliberate simplicity choice over diff --git a/docs/architecture.md b/docs/architecture.md index 5383485..7d18052 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,4 +1,4 @@ -# Sentry Architecture +# Cairn OBS Architecture > **Status:** Updated through Phase 4. The component map/diagram below is > still the Phase 0 request path (agent → ingest → ClickHouse → api → diff --git a/docs/compliance/license-audit-report.md b/docs/compliance/license-audit-report.md index c6011d1..b5a675c 100644 --- a/docs/compliance/license-audit-report.md +++ b/docs/compliance/license-audit-report.md @@ -28,7 +28,7 @@ like BSL/SSPL, anything with a field-of-use or non-compete restriction). 1. **Inventory** every dependency, direct and transitive, in every language ecosystem present in the repo: - - **Rust** (`agent` workspace: `sentry-agent`, `sentry-parser`; + - **Rust** (`agent` workspace: `cairnobs-agent`, `cairnobs-parser`; `search`): `cargo-deny` (`cargo deny list --format tsv`), installed fresh for this audit (`cargo install cargo-deny --locked`). - **Go** (`api`, `ingest`, `alerting`, `enterprise`, @@ -191,18 +191,18 @@ Key facts, verified against primary sources: plaintext core Kafka-protocol functionality — no RCL-gated enterprise features, no tiered storage, no SASL/RBAC — squarely within the permitted grant as an internal transport layer. -- **No AGPL linking/compatibility issue.** Sentry never links against +- **No AGPL linking/compatibility issue.** Cairn OBS never links against Redpanda's code; it's consumed purely over the Kafka wire protocol, the same relationship as ClickHouse and Postgres. AGPLv3's copyleft doesn't reach across a network-protocol boundary to unrelated, separately-licensed software you merely talk to. - **The genuinely open question**: Phase 6 relicenses `enterprise/` to AGPLv3 specifically so that anyone, including competitors, can legally - self-host or fork Sentry — including offering it as a network service, + self-host or fork Cairn OBS — including offering it as a network service, per AGPLv3's own terms. If a third party does that using the bundled `docker-compose.yml` (which pulls this BSL-licensed Redpanda image), does *their* deployment trip BSL's Streaming-or-Queuing-Service - restriction? Sentry's ingest pipeline creates fixed internal topics, + restriction? Cairn OBS's ingest pipeline creates fixed internal topics, not per-end-user topics exposed for direct third-party production or consumption — so this is very likely **not** a Streaming-or-Queuing-Service under BSL's own definition. But this is a business/redistribution @@ -211,7 +211,7 @@ Key facts, verified against primary sources: ### Remediation options (recorded per task 4's requirement) -1. **Accept as-is.** Document the reasoning above; Sentry's own use is +1. **Accept as-is.** Document the reasoning above; Cairn OBS's own use is clearly within BSL's permitted grant, and the third-party-SaaS scenario is a reasonable-but-unverified reading, not a known violation. Lowest effort, zero functional change. @@ -241,22 +241,23 @@ deployment change was made as a result — Redpanda stays pinned at v24.2.7 in `docker-compose.yml`/`transport/`, under BSL 1.1, as a disclosed and accepted risk rather than an unresolved one. This decision should be revisited if the project's redistribution posture -changes materially (e.g. an official hosted/managed offering of Sentry -itself, which would make the third-party-SaaS reading in this section -Sentry's *own* situation rather than a hypothetical third party's). +changes materially (e.g. an official hosted/managed offering of Cairn +OBS itself, which would make the third-party-SaaS reading in this section +Cairn OBS's *own* situation rather than a hypothetical third party's). ## Non-license finding: `favicon.svg` -`web/src/lib/assets/favicon.svg` is SvelteKit's own default project +`web/src/lib/assets/favicon.svg` was SvelteKit's own default project scaffold logo (`svelte-logo` — the `sv create`/`create-svelte` starter icon), never replaced with an original mark during Phase 5's redesign. Not a license-compatibility blocker — Svelte's own project -assets are MIT-licensed — but it's unauthored, third-party-branded +assets are MIT-licensed — but it was unauthored, third-party-branded content shipping as this product's own favicon, caught by the same "grep for anything that looks copied" pass this audit's task 1 asked -for. Recorded as an action item (replace with an original Sentry mark), -not a compliance blocker; not fixed here since it's a design task outside -this phase's scope, not a licensing one. +for. Recorded as an action item (replace with an original mark), not a +compliance blocker at the time; **resolved as part of the Sentry → Cairn +OBS rebrand**, which replaced it with the real Cairn OBS mark from the +project's own logo package. ## Own license declarations (task 5) @@ -347,7 +348,7 @@ assumed. - Final repo-wide grep for `commercial` confirms every remaining occurrence is one of the above corrections (explicitly framed as historical/superseded), not a live claim. **No file in the repo claims - a license other than AGPLv3** for Sentry's own code, as of this audit. + a license other than AGPLv3** for Cairn OBS's own code, as of this audit. ## Ongoing enforcement (task 7) diff --git a/docs/compliance/license-policy.md b/docs/compliance/license-policy.md index 505dc92..fed5626 100644 --- a/docs/compliance/license-policy.md +++ b/docs/compliance/license-policy.md @@ -97,7 +97,7 @@ this project uses it. **Decision recorded 2026-08-16: accept as-is** — see the audit report's Redpanda section for the full reasoning, the other two remediation options that were considered and not chosen, and the condition under which this decision should be revisited (an -official hosted/managed Sentry offering). A future change to Redpanda's +official hosted/managed Cairn OBS offering). A future change to Redpanda's license, or to this project's own redistribution posture, should trigger re-review, not silently ride on this entry. diff --git a/docs/design-system.md b/docs/design-system.md index a5358cc..a2527c2 100644 --- a/docs/design-system.md +++ b/docs/design-system.md @@ -1,4 +1,4 @@ -# Sentry Design System (Phase 5) +# Cairn OBS Design System (Phase 5) Direction: **Signal** — cockpit/ICU-monitor instrumentation logic. Color is a rationed resource: the UI is near-neutral grayscale everywhere, so @@ -140,7 +140,7 @@ Two presets, one token swap — `html.density-compact` overrides and drops `--text-base` to `--text-sm`. Comfortable (the default) suits dashboards and forms; compact suits log tables and query results. Toggle via `$lib/density.svelte.ts`'s `setDensity()`/`toggleDensity()` — a -global, persisted (`localStorage['sentry.density']`) preference, not a +global, persisted (`localStorage['cairnobs.density']`) preference, not a per-page setting, so switching it on one page carries to the next. `web/src/app.html` has a synchronous inline script that applies the stored value before first paint, so there's no flash of the wrong diff --git a/docs/phase-0-runbook.md b/docs/phase-0-runbook.md index 04f0e31..62428b7 100644 --- a/docs/phase-0-runbook.md +++ b/docs/phase-0-runbook.md @@ -85,15 +85,15 @@ any agent has sent data. ## 4. Install the agent's mTLS material -The agent's default config expects certs at `/etc/sentry-agent/` (see +The agent's default config expects certs at `/etc/cairnobs-agent/` (see `/agent/config/agent.example.toml`), which requires root: ```sh -sudo mkdir -p /etc/sentry-agent +sudo mkdir -p /etc/cairnobs-agent sudo cp hack/dev-certs/out/ca.pem \ hack/dev-certs/out/client.pem \ hack/dev-certs/out/client-key.pem \ - /etc/sentry-agent/ + /etc/cairnobs-agent/ ``` ## 5. Build and run the agent @@ -116,7 +116,7 @@ Reading the system journal generally needs root (or membership in the by distro, root is the reliable path for this runbook): ```sh -sudo RUST_LOG=info ./target/release/sentry-agent +sudo RUST_LOG=info ./target/release/cairnobs-agent ``` `RUST_LOG=info` matters: `tracing_subscriber`'s default filter is diff --git a/docs/phase-1-runbook.md b/docs/phase-1-runbook.md index af20ee3..1ee14ae 100644 --- a/docs/phase-1-runbook.md +++ b/docs/phase-1-runbook.md @@ -128,14 +128,14 @@ compile time), same requirement as the Linux build. ### C2. Get mTLS certs onto the Windows host Copy `hack/dev-certs/out/{ca,client,client-key}.pem` from wherever you -ran `generate.sh` to `C:\ProgramData\SentryAgent\` on the Windows host +ran `generate.sh` to `C:\ProgramData\CairnObsAgent\` on the Windows host (create the directory first). Same dev-only certs Phase 0's Linux agent uses — the CA doesn't care what platform the client is on, only that the client cert was signed by it. ### C3. Config -Create `C:\ProgramData\SentryAgent\agent.toml`: +Create `C:\ProgramData\CairnObsAgent\agent.toml`: ```toml [source] @@ -155,7 +155,7 @@ needed (same note as Phase 0's runbook's troubleshooting section). ```powershell $env:RUST_LOG="info" -.\sentry-agent.exe --config C:\ProgramData\SentryAgent\agent.toml +.\cairnobs-agent.exe --config C:\ProgramData\CairnObsAgent\agent.toml ``` Confirms the Event Log source and mTLS connection work before adding the @@ -167,7 +167,7 @@ diagnose here than after wrapping it in a service. From another PowerShell window (or Event Viewer): ```powershell -eventcreate /T INFORMATION /ID 1 /L APPLICATION /SO "SentryTest" /D "phase1 windows verification line" +eventcreate /T INFORMATION /ID 1 /L APPLICATION /SO "CairnObsTest" /D "phase1 windows verification line" ``` Then check both query paths, same pattern as A2. @@ -175,11 +175,11 @@ Then check both query paths, same pattern as A2. ### C6. Install as a service ```powershell -.\sentry-agent.exe install -sc.exe start SentryAgent +.\cairnobs-agent.exe install +sc.exe start CairnObsAgent ``` -Verify it's running (`sc.exe query SentryAgent`) and generate another +Verify it's running (`sc.exe query CairnObsAgent`) and generate another test event to confirm it's still flowing through while running as a service, not just in the foreground. **Known gap:** no console under the SCM means `tracing`'s log output currently has nowhere to go — see @@ -188,8 +188,8 @@ something goes wrong here, you're debugging blind until that's addressed; C4's foreground run is where to diagnose real problems. ```powershell -sc.exe stop SentryAgent -.\sentry-agent.exe uninstall +sc.exe stop CairnObsAgent +.\cairnobs-agent.exe uninstall ``` ### C7 (optional). ETW diff --git a/docs/phase-2-runbook.md b/docs/phase-2-runbook.md index fa5b6ec..7e5ce1e 100644 --- a/docs/phase-2-runbook.md +++ b/docs/phase-2-runbook.md @@ -136,7 +136,7 @@ in the session-local history panel. ```sh cd cli -go run ./cmd/sentryctl query 'service=api | where status>=500 | stats count by host | sort -count' +go run ./cmd/cairnobsctl query 'service=api | where status>=500 | stats count by host | sort -count' ``` Both hit the exact same `POST /query` endpoint — there's no separate diff --git a/docs/phase-3-alerting-design.md b/docs/phase-3-alerting-design.md index 481e0c1..1023f82 100644 --- a/docs/phase-3-alerting-design.md +++ b/docs/phase-3-alerting-design.md @@ -297,7 +297,7 @@ Loop shape: up to a bounded batch size. 2. Dispatch claimed rules to a bounded worker pool (goroutines). 3. Each worker: `POST /query` against `api` (reusing the existing - endpoint — the same precedent `sentryctl query` already set, never a + endpoint — the same precedent `cairnobsctl query` already set, never a second query-execution path), evaluate the condition, run the state transition (fix 3/4 aware) and, if applicable, the fix-2 transactional outbox insert, in one short DB transaction. @@ -319,7 +319,7 @@ loop, and the delivery worker — all four pieces of "alerting" in one service, since they share the same Postgres tables and the same claim-based concurrency pattern. It does **not** import `api`'s `querylang` package or talk to ClickHouse/Tantivy directly; it only -calls `api`'s `POST /query` over HTTP, exactly like `sentryctl query` +calls `api`'s `POST /query` over HTTP, exactly like `cairnobsctl query` and the web UI's dashboard panels already do. `web` gets a second backend base URL (`alerting`'s) alongside the existing `api` one. diff --git a/docs/phase-3-dashboard-design.md b/docs/phase-3-dashboard-design.md index 0c6c64a..358fedc 100644 --- a/docs/phase-3-dashboard-design.md +++ b/docs/phase-3-dashboard-design.md @@ -206,7 +206,7 @@ per panel directly, exactly the same endpoint the root query page already uses. This keeps `api/internal/dashboards` pure CRUD with zero query-execution code of its own, consistent with the project's standing rule that nothing duplicates `querylang`'s execution path (the same -reason `sentryctl query` calls `/query` over HTTP instead of importing +reason `cairnobsctl query` calls `/query` over HTTP instead of importing `querylang` internals). It also means panels load and error independently in the UI — one broken panel query doesn't fail the whole dashboard. @@ -253,7 +253,7 @@ used internally, not a separate export-specific shape). `POST /dashboards/import` accepts that same shape and creates a new dashboard from it (new IDs assigned, not a literal replay of the source IDs, so importing into a different environment doesn't collide). This is -deliberately the *same* JSON contract `sentryctl dashboards apply` (task +deliberately the *same* JSON contract `cairnobsctl dashboards apply` (task 7) consumes — "exportable/importable, Terraform-friendly" isn't a separate design, it's one JSON shape used from two call sites (web export button, CLI apply). diff --git a/docs/phase-4-runbook.md b/docs/phase-4-runbook.md index d15876c..77d36ba 100644 --- a/docs/phase-4-runbook.md +++ b/docs/phase-4-runbook.md @@ -95,7 +95,7 @@ memberships and getting back the right tenant/role each time (see §3a, `helm` were installed without root (`kind`/`kubectl` as static binaries, `helm` the same, all into `~/.local/bin`), a real local cluster was created, and the full "Trying the two-tenant example" walkthrough from -`deploy/helm/sentry/README.md` was run end to end against it -- both +`deploy/helm/cairnobs/README.md` was run end to end against it -- both `acme` and `globex` reached `Tenant.status.phase: Active` with real generated ClickHouse credentials in their Secrets. That run found and fixed two more real bugs, neither ever caught before because this chart @@ -138,7 +138,7 @@ New service beyond Phase 3: `enterprise-auth` (port 8082) — see `enterprise/README.md`. Not wired into `api`/`alerting`'s enforcement by default (`docker-compose.yml`'s comment on why: no OIDC/SAML login flow exists yet, so turning on enforcement by default would break the web UI -and `sentryctl` with no way to log in). +and `cairnobsctl` with no way to log in). ## 2. Confirm Phase 0-3 behavior is unchanged @@ -148,7 +148,7 @@ through every piece of Phase 4 auth wiring: ```sh curl -s -X POST http://localhost:8080/query -H 'Content-Type: application/json' -d '{"query":"stats count"}' -sentryctl dashboards list +cairnobsctl dashboards list curl -s http://localhost:8081/healthz ``` @@ -192,7 +192,7 @@ creating the `users` row via `UpsertUserBySSO`, exactly as designed; after `-grant-membership-*` granted that real Auth0 identity (`john@linuxexperts.net`) an `admin` role on `acme`, a second login completed the full OAuth code exchange, consent screen, and redirect to -`web`, landing a real `sentry_session` cookie; `POST +`web`, landing a real `cairnobs_session` cookie; `POST /internal/authorize` with that cookie returned `{"tenant_id":"acme","user_id":"...","role":"admin"}` -- the exact membership granted, round-tripped through a real external IdP, not a @@ -232,7 +232,7 @@ docker compose run --rm enterprise-auth \ Then visit `http://localhost:8082/auth/oidc/login` again in a real browser, complete the IdP's login, and confirm you land on `POST_LOGIN_REDIRECT_URL` (`http://localhost:3000` by default) with a -`sentry_session` cookie set. `-create-tenant` only touches `rbacstore` +`cairnobs_session` cookie set. `-create-tenant` only touches `rbacstore` (control-plane/RBAC) -- it's independent of `enterprise-api -provision-tenant`'s ClickHouse/Tantivy data-plane provisioning (§8), so a tenant created this way can log users in immediately but can't yet @@ -243,7 +243,7 @@ cover revoking, listing, and reassigning ownership the same way `-grant-membership-*` covers granting (all offline operator flags, same shape as `-create-tenant`) -- `dashboard_permissions` grants (§5a) are the one thing here still only reachable via the HTTP endpoints -directly, no operator flag, since `sentryctl dashboards permissions +directly, no operator flag, since `cairnobsctl dashboards permissions list|grant|revoke` already exists as that surface instead (§5a). ## 3b. `enterprise-auth`: human login via SAML (now genuinely verified @@ -294,7 +294,7 @@ offer a free developer/trial tenant with SAML app support): # SAML_IDP_METADATA_URL: "https://your-idp.example.com/metadata" # Register SAML_ENTITY_ID (as "audience")/SAML_ACS_URL (as the # "Application Callback URL") with the IdP's application config -- the -# IdP needs Sentry's ACS URL to know where to POST the assertion, and +# IdP needs Cairn OBS's ACS URL to know where to POST the assertion, and # the two must actually agree or the SP-side validation fails with a # generic "Authentication failed" (found the hard way -- crewjam/saml's # error here doesn't distinguish audience mismatch from other causes). @@ -309,7 +309,7 @@ Bootstrapping the first `tenant_memberships` row uses the same `-create-tenant`/`-grant-membership-*` flags as §3a (log in once, it fails with 403, grant the membership using the email you logged in with, log in again). Then visit `/auth/saml/login` on your HTTPS host in -a real browser, complete the IdP's login, and confirm a `sentry_session` +a real browser, complete the IdP's login, and confirm a `cairnobs_session` cookie lands after redirect to `POST_LOGIN_REDIRECT_URL`. ## 4. Turn on RBAC enforcement and prove it actually blocks/allows @@ -318,7 +318,7 @@ Without touching the main stack's `api` container (so step 2's baseline keeps working): ```sh -docker compose run --rm -d --name sentry-api-enforced -p 8090:8080 \ +docker compose run --rm -d --name cairnobs-api-enforced -p 8090:8080 \ -e ENTERPRISE_AUTH_URL=http://enterprise-auth:8082 api curl -s -o /dev/null -w "no auth -> %{http_code} (want 401)\n" \ @@ -327,7 +327,7 @@ curl -s -o /dev/null -w "with service token -> %{http_code} (want 200)\n" \ -X POST http://localhost:8090/query -H "Authorization: Bearer $TOKEN" \ -H 'Content-Type: application/json' -d '{"query":"stats count"}' -docker stop sentry-api-enforced +docker stop cairnobs-api-enforced ``` `GET /dashboards` on the same enforced instance should return 401 @@ -346,7 +346,7 @@ tenant. Verify the real SQL, not just the fake-store unit tests: ```sh docker run --rm --network sentry_default -v $(pwd)/api:/src -w /src \ -e DASHBOARDS_TEST_POSTGRES_ADDR=metadata-postgres:5432 \ - -e DASHBOARDS_TEST_POSTGRES_PASSWORD=sentry-dev-only \ + -e DASHBOARDS_TEST_POSTGRES_PASSWORD=cairnobs-dev-only \ golang:1.25-alpine go test ./dashboards/... -run Integration -v ``` @@ -375,7 +375,7 @@ PermissionStore`) have real integration tests, same skip-gated shape as ```sh docker run --rm --network sentry_default -v $(pwd):/src -w /src/enterprise \ -e RBACSTORE_TEST_POSTGRES_ADDR=metadata-postgres:5432 \ - -e RBACSTORE_TEST_POSTGRES_PASSWORD=sentry-dev-only \ + -e RBACSTORE_TEST_POSTGRES_PASSWORD=cairnobs-dev-only \ golang:1.25-alpine go test ./internal/rbacstore/... -run DashboardPermission -v ``` @@ -396,13 +396,13 @@ when `enterprise-api` (not plain `api`) is serving traffic -- see ```sh docker run --rm --network sentry_default -v $(pwd):/src -w /src/enterprise \ -e RBACSTORE_TEST_POSTGRES_ADDR=metadata-postgres:5432 \ - -e RBACSTORE_TEST_POSTGRES_PASSWORD=sentry-dev-only \ + -e RBACSTORE_TEST_POSTGRES_PASSWORD=cairnobs-dev-only \ golang:1.25-alpine go test ./internal/rbacstore/... -v docker run --rm --network sentry_default -v $(pwd):/src -w /src/enterprise \ -e AUDIT_TEST_POSTGRES_ADDR=metadata-postgres:5432 \ -e AUDIT_TEST_POSTGRES_PASSWORD=audit-writer-dev-only \ - -e AUDIT_TEST_ADMIN_PASSWORD=sentry-dev-only \ + -e AUDIT_TEST_ADMIN_PASSWORD=cairnobs-dev-only \ golang:1.25-alpine go test ./internal/audit/... -v ``` @@ -419,10 +419,10 @@ Offline checks (no cluster needed): ```sh cd deploy/operator && go build ./... && go vet ./... && go test ./... -cd ../helm/sentry +cd ../helm/cairnobs helm lint . -helm template sentry . --include-crds > /tmp/default.yaml -helm template sentry . --include-crds \ +helm template cairnobs . --include-crds > /tmp/default.yaml +helm template cairnobs . --include-crds \ --set enterprise.enabled=true --set tenantOperator.enabled=true \ --set 'tenants[0].name=acme' --set 'tenants[0].displayName=Acme Corp' \ --set 'tenants[1].name=globex' --set 'tenants[1].displayName=Globex Corporation' \ @@ -434,25 +434,25 @@ without root (`kind`/`kubectl`/`helm` as static binaries into e.g. `~/.local/bin`; no package manager or sudo needed): ```sh -kind create cluster --name sentry-phase4 +kind create cluster --name cairnobs-phase4 kubectl wait --for=condition=Ready node --all --timeout=120s # Build and load every image the chart references -- kind's nodes can't # pull unpublished local-only images from a registry, only from images # already loaded into the node directly. -docker build -f deploy/operator/Dockerfile -t sentry-tenant-operator deploy/operator/ -for img in sentry-redpanda-provision sentry-clickhouse-migrate sentry-metadata-migrate \ - sentry-ingest sentry-search sentry-api sentry-alerting sentry-web \ - sentry-enterprise-auth sentry-enterprise-api sentry-enterprise-ingest \ - sentry-tenant-operator; do - kind load docker-image "${img}:latest" --name sentry-phase4 +docker build -f deploy/operator/Dockerfile -t cairnobs-tenant-operator deploy/operator/ +for img in cairnobs-redpanda-provision cairnobs-clickhouse-migrate cairnobs-metadata-migrate \ + cairnobs-ingest cairnobs-search cairnobs-api cairnobs-alerting cairnobs-web \ + cairnobs-enterprise-auth cairnobs-enterprise-api cairnobs-enterprise-ingest \ + cairnobs-tenant-operator; do + kind load docker-image "${img}:latest" --name cairnobs-phase4 done # NOTE: helm install has no --include-crds flag (that's a helm template-only # flag -- install always installs crds/ by default). The command in -# deploy/helm/sentry/README.md's "Trying the two-tenant example" had this +# deploy/helm/cairnobs/README.md's "Trying the two-tenant example" had this # wrong; fixed there too. -helm install sentry deploy/helm/sentry \ +helm install cairnobs deploy/helm/cairnobs \ --set enterprise.enabled=true --set tenantOperator.enabled=true \ --set 'tenants[0].name=acme' --set 'tenants[0].displayName=Acme Corp' \ --set 'tenants[1].name=globex' --set 'tenants[1].displayName=Globex Corporation' @@ -467,12 +467,12 @@ equivalent) rather than hand-rolling it in the chart, so a Secret needs supplying before `ingest` can start: ```sh -kubectl create secret generic sentry-ingest-tls \ +kubectl create secret generic cairnobs-ingest-tls \ --from-file=server.pem=hack/dev-certs/out/server.pem \ --from-file=server-key.pem=hack/dev-certs/out/server-key.pem \ --from-file=ca.pem=hack/dev-certs/out/ca.pem -helm upgrade sentry deploy/helm/sentry --reuse-values \ - --set ingest.tlsSecretName=sentry-ingest-tls +helm upgrade cairnobs deploy/helm/cairnobs --reuse-values \ + --set ingest.tlsSecretName=cairnobs-ingest-tls ``` Confirm every pod actually reaches `Running`/`1/1` (`kubectl get pods`) @@ -486,10 +486,10 @@ missing `CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT` env var (same bug §1's `docker-compose.yml` had). Both fixed in the chart itself. ```sh -kubectl exec -it deploy/sentry-api -- /enterprise-api -provision-tenant=acme -display-name="Acme Corp" -kubectl exec -it deploy/sentry-api -- /enterprise-api -provision-tenant=globex -display-name="Globex Corporation" +kubectl exec -it deploy/cairnobs-api -- /enterprise-api -provision-tenant=acme -display-name="Acme Corp" +kubectl exec -it deploy/cairnobs-api -- /enterprise-api -provision-tenant=globex -display-name="Globex Corporation" kubectl get tenants -kubectl get secret sentry-tenant-acme-clickhouse sentry-tenant-globex-clickhouse -o yaml +kubectl get secret cairnobs-tenant-acme-clickhouse cairnobs-tenant-globex-clickhouse -o yaml ``` **Confirmed live**: `kubectl get tenants` shows both `acme` and `globex` @@ -538,12 +538,12 @@ walkthrough isn't scripted yet): ```sh docker run --rm --network sentry_default -v $(pwd)/enterprise:/src -w /src \ -e CHRUNNER_TEST_CLICKHOUSE_ADDR=clickhouse:9000 \ - -e CHRUNNER_TEST_CLICKHOUSE_PASSWORD=sentry-dev-only \ + -e CHRUNNER_TEST_CLICKHOUSE_PASSWORD=cairnobs-dev-only \ golang:1.25-alpine go test ./internal/chrunner/... -v docker run --rm --network sentry_default -v $(pwd)/enterprise:/src -w /src \ -e TENANTPROVISION_TEST_CLICKHOUSE_ADDR=clickhouse:9000 \ - -e TENANTPROVISION_TEST_CLICKHOUSE_PASSWORD=sentry-dev-only \ + -e TENANTPROVISION_TEST_CLICKHOUSE_PASSWORD=cairnobs-dev-only \ golang:1.25-alpine go test ./internal/tenantprovision/... -v ``` @@ -606,9 +606,9 @@ No live cluster needed for this either — `helm template`'s output is plain YAML, parseable without a cluster: ```sh -cd deploy/helm/sentry -helm template sentry . --include-crds > /tmp/default.yaml -helm template sentry . --include-crds --set enterprise.enabled=true \ +cd deploy/helm/cairnobs +helm template cairnobs . --include-crds > /tmp/default.yaml +helm template cairnobs . --include-crds --set enterprise.enabled=true \ --set 'tenants[0].name=acme' --set 'tenants[0].displayName=Acme Corp' \ > /tmp/enterprise.yaml @@ -616,17 +616,17 @@ python3 -c " import yaml for f in ['/tmp/default.yaml', '/tmp/enterprise.yaml']: docs = list(yaml.safe_load_all(open(f))) - deploys = [d for d in docs if d and d.get('kind')=='Deployment' and d.get('metadata',{}).get('name')=='sentry-api'] + deploys = [d for d in docs if d and d.get('kind')=='Deployment' and d.get('metadata',{}).get('name')=='cairnobs-api'] print(f, '->', [d['spec']['template']['spec']['containers'][0]['image'] for d in deploys]) " -# expect: default.yaml -> ['sentry-api:latest'], enterprise.yaml -> ['sentry-enterprise-api:latest'] -# and exactly one Deployment named sentry-api in each file. +# expect: default.yaml -> ['cairnobs-api:latest'], enterprise.yaml -> ['cairnobs-enterprise-api:latest'] +# and exactly one Deployment named cairnobs-api in each file. ``` Real cluster (`kind create cluster`, or similar): `helm install` with -each set of values and confirm `kubectl get deploy sentry-api -o +each set of values and confirm `kubectl get deploy cairnobs-api -o jsonpath='{.spec.template.spec.containers[0].image}'` matches, and that -`kubectl get svc sentry-api` routes to whichever one is actually running. +`kubectl get svc cairnobs-api` routes to whichever one is actually running. ## 10a. Confirm `docker-compose.yml` now enforces the same binary swap @@ -668,7 +668,7 @@ actually start and route traffic correctly end to end. ## 11. `Tenant` CRD unified with `-provision-tenant` -`deploy/helm/sentry/README.md`'s "Trying the two-tenant example" section +`deploy/helm/cairnobs/README.md`'s "Trying the two-tenant example" section has the full `helm install` → `-provision-tenant` → `kubectl get tenants` walkthrough. What was actually run in this environment (no live cluster, same limitation as §10): @@ -762,7 +762,7 @@ through `mcp__claude-in-chrome`: 1. A throwaway Node HTTP server (no dependencies) stood in for `enterprise-auth`, implementing the exact wire contract this section's Go tests already prove server-side: `GET /auth/memberships` and - `POST /auth/select-tenant`, the `sentry_pending_login` cookie + `POST /auth/select-tenant`, the `cairnobs_pending_login` cookie (`Path=/auth`), the credentialed CORS headers, and critically the *plain-text* `http.Error` response bodies the real handler sends on failure (not JSON -- `enterpriseAuthRequest` in `$lib/api.ts` reads @@ -857,7 +857,7 @@ Also not built as part of the identity mechanism itself: any Helm/ `docker-compose.yml` wiring that issues an agent a real ingest credential automatically (`enterprise-auth -create-ingest-credential-tenant=` is, like every other credential-minting flag in this codebase, a manual -operator action) -- `deploy/helm/sentry/values.yaml`'s +operator action) -- `deploy/helm/cairnobs/values.yaml`'s `ingest.requireTenantCredential` (default `false`) only turns on *validation*, deliberately not folded into `enterprise.enabled` directly, since flipping that flag with no agents holding a credential @@ -1022,7 +1022,7 @@ that's never executed against a real database. See **Also not built**: Helm/`docker-compose.yml` do gate *whether* `enterprise-ingest` runs at all (`ingest.requireTenantCredential`, same -flag §13 uses for validation -- see `deploy/helm/sentry/templates/ +flag §13 uses for validation -- see `deploy/helm/cairnobs/templates/ enterprise-ingest.yaml`), but `docker-compose.yml`'s version is a disclosed, weaker approximation of Helm's: Helm achieves genuine `-mode=server`/`-mode=consumer` mutual exclusivity between `ingest` and @@ -1040,7 +1040,7 @@ Full accounting: `/docs/security/threat-model.md`. Headline items: - **Both storage engines' isolation exists, and both Helm and docker-compose now enforce which binary runs.** - `deploy/helm/sentry/templates/api.yaml`/`enterprise-api.yaml` are + `deploy/helm/cairnobs/templates/api.yaml`/`enterprise-api.yaml` are mutually exclusive on `enterprise.enabled` (§10) -- a Helm-deployed cluster can't accidentally run the non-isolated binary once that flag is set. `docker-compose.yml`'s `api`/`enterprise-api` services are now @@ -1120,14 +1120,14 @@ Full accounting: `/docs/security/threat-model.md`. Headline items: handler reads `dashboard_permissions` via `enterprise/internal/rbacstore.DashboardPermissions`, only when `enterprise-api` -- not plain `api` -- serves traffic), **and now has a - CLI surface**: `sentryctl dashboards permissions list|grant|revoke` - (`cli/cmd/sentryctl/cmd_dashboards.go`) against + CLI surface**: `cairnobsctl dashboards permissions list|grant|revoke` + (`cli/cmd/cairnobsctl/cmd_dashboards.go`) against `GET`/`PUT`/`DELETE /dashboards/{id}/permissions/{userId}` -- still no `web` UI for it, just the CLI. Verified against a real `httptest. Server` (not a fake store this time -- the CLI has no store of its own, just an HTTP client, so this is exercising real request construction/method/path/body/error-parsing, the same pattern every - other `sentryctl` subcommand's tests use): + other `cairnobsctl` subcommand's tests use): ```sh cd cli @@ -1162,7 +1162,7 @@ Full accounting: `/docs/security/threat-model.md`. Headline items: ```sh docker compose down -v -helm uninstall sentry # if installed against a real cluster +helm uninstall cairnobs # if installed against a real cluster ``` ## Troubleshooting @@ -1191,6 +1191,6 @@ parameter. **`helm template` fails with `error calling include: ... can't evaluate field Release in type string`.** -A call site is passing a bare string to `sentry.selectorLabels` instead +A call site is passing a bare string to `cairnobs.selectorLabels` instead of `(list $ "name")` — see `templates/_helpers.tpl`'s doc comment for why the plain-string form doesn't work with `include`. diff --git a/docs/phase-7-ai-design.md b/docs/phase-7-ai-design.md index 84428fa..c3a086f 100644 --- a/docs/phase-7-ai-design.md +++ b/docs/phase-7-ai-design.md @@ -63,7 +63,7 @@ Design choices worth calling out explicitly: | Throughput under concurrency | Adequate for one-user-at-a-time interactive use; not built for high concurrent QPS | Purpose-built for high-throughput serving (continuous batching, PagedAttention) | | Fit for this project | Matches `docker-compose for local/homelab` (CLAUDE.md's stated deployment target) — most self-hosters won't have a dedicated inference GPU | Fits a provisioned-GPU SaaS inference tier — not this phase's target (cloud is the opt-in secondary path, not primary) | -Sentry's actual AI workload shape is one interactive query bar per user +Cairn OBS's actual AI workload shape is one interactive query bar per user at a time, not a high-QPS inference-serving problem — vLLM's real advantage (batched throughput at scale) isn't the bottleneck this phase has. Ollama's lower hardware floor and much simpler operational story diff --git a/docs/phase-7-runbook.md b/docs/phase-7-runbook.md index b990b3c..e0b9fc4 100644 --- a/docs/phase-7-runbook.md +++ b/docs/phase-7-runbook.md @@ -191,7 +191,7 @@ cd enterprise && go build ./... && go vet ./... && go test ./... docker run --rm --network sentry_default -v "$(pwd):/src" -w /src/enterprise \ -e AUDIT_TEST_POSTGRES_ADDR=metadata-postgres:5432 \ -e AUDIT_TEST_POSTGRES_PASSWORD=audit-writer-dev-only \ - -e AUDIT_TEST_ADMIN_PASSWORD=sentry-dev-only \ + -e AUDIT_TEST_ADMIN_PASSWORD=cairnobs-dev-only \ golang:1.25-alpine go test ./internal/audit/... -v # cli module diff --git a/docs/query-language-reference.md b/docs/query-language-reference.md index 7634772..97bf45c 100644 --- a/docs/query-language-reference.md +++ b/docs/query-language-reference.md @@ -1,16 +1,16 @@ # Query language reference -Sentry has one query language for everything: filtering, free-text +Cairn OBS has one query language for everything: filtering, free-text search, and aggregation, in a single query, against a single endpoint -(`POST /query`), from a single query bar in the web UI or `sentryctl +(`POST /query`), from a single query bar in the web UI or `cairnobsctl query` on the command line. You don't pick a "search mode" or a -"reporting mode" first — you write one query, and Sentry figures out +"reporting mode" first — you write one query, and Cairn OBS figures out which parts need ClickHouse, which parts need the full-text index, and combines them. If you already know Splunk's SPL, most of this will feel immediately familiar: a base search, piped through a sequence of processing stages. -Sentry's language is a deliberately smaller subset — the operators +Cairn OBS's language is a deliberately smaller subset — the operators people actually use day to day, not SPL's full surface area — plus raw SQL as an escape hatch for anything the pipe syntax doesn't (yet) cover. @@ -67,7 +67,7 @@ timeout a single bare word message:"connection refused" the same thing, explicit ``` -Free-text search is powered by Sentry's full-text index (Tantivy), which +Free-text search is powered by Cairn OBS's full-text index (Tantivy), which supports phrase matching and wildcards: ``` @@ -92,7 +92,7 @@ what most people expect from a search bar: error timeout same as: error and timeout ``` -`or` works between free-text terms, and Sentry's full-text index handles +`or` works between free-text terms, and Cairn OBS's full-text index handles it natively: ``` @@ -176,14 +176,14 @@ tail 50 last 50, chronologically ## Field mapping: what's a "real" column vs. an attribute -Sentry's structured columns are `timestamp`, `host`, `service`, +Cairn OBS's structured columns are `timestamp`, `host`, `service`, `severity`, `message`, and `record_id`. Anything else you reference by name — `status`, `latency_ms`, `winevt.event_id`, whatever your logs happen to carry — is looked up in the per-record attributes, which are always stored as text. This matters for comparisons: `status>=500` only makes sense as a number, -so Sentry casts the attribute's text value to a number for you +so Cairn OBS casts the attribute's text value to a number for you automatically when the value you're comparing against looks numeric. `status="unknown"` compares as text instead, since `"unknown"` isn't a number. You don't need to do anything differently — this happens based @@ -212,7 +212,7 @@ directly, no pipe-syntax parsing involved: SELECT host, count(*) FROM logs WHERE service = 'api' GROUP BY host ``` -SELECT-only, single statement — Sentry allowlists this at the API level. +SELECT-only, single statement — Cairn OBS allowlists this at the API level. Use this for anything the pipe syntax doesn't cover yet: window functions, `WITH` clauses, ClickHouse-specific functions, joins across other tables you've added, and so on. There's no performance penalty for @@ -221,7 +221,7 @@ execution plan internally. ## Which syntax am I using? -Sentry detects automatically: a query starting with `SELECT` runs as +Cairn OBS detects automatically: a query starting with `SELECT` runs as SQL, anything else runs as the pipe syntax. This covers the overwhelming majority of real queries with no extra step. If you're writing a pipe query that happens to start with the literal word "select" as a search @@ -237,7 +237,7 @@ detected next to the query box, with a dropdown to override it. ## Combining free-text search with aggregation -This is the case that makes Sentry's query language more than "SQL with +This is the case that makes Cairn OBS's query language more than "SQL with extra steps" — free text and aggregation, together, in one query: ``` diff --git a/docs/security/threat-model.md b/docs/security/threat-model.md index 83565da..408db70 100644 --- a/docs/security/threat-model.md +++ b/docs/security/threat-model.md @@ -1,4 +1,4 @@ -# Sentry Threat Model (Phase 4) +# Cairn OBS Threat Model (Phase 4) Written for a prospective enterprise customer's security team, describing the system **as actually built** through Phase 4 task 7 — not the target diff --git a/enterprise/README.md b/enterprise/README.md index 042823e..5a72c1a 100644 --- a/enterprise/README.md +++ b/enterprise/README.md @@ -372,7 +372,7 @@ integration tests are skipped by default and only run with docker run --rm --network sentry_default -v $(pwd)/..:/src -w /src/enterprise \ -e AUDIT_TEST_POSTGRES_ADDR=metadata-postgres:5432 \ -e AUDIT_TEST_POSTGRES_PASSWORD=audit-writer-dev-only \ - -e AUDIT_TEST_ADMIN_PASSWORD=sentry-dev-only \ + -e AUDIT_TEST_ADMIN_PASSWORD=cairnobs-dev-only \ golang:1.25-alpine go test ./internal/audit/... -v ``` @@ -382,7 +382,7 @@ constraints), skipped unless `RBACSTORE_TEST_POSTGRES_ADDR` is set: ```sh docker run --rm --network sentry_default -v $(pwd)/..:/src -w /src/enterprise \ -e RBACSTORE_TEST_POSTGRES_ADDR=metadata-postgres:5432 \ - -e RBACSTORE_TEST_POSTGRES_PASSWORD=sentry-dev-only \ + -e RBACSTORE_TEST_POSTGRES_PASSWORD=cairnobs-dev-only \ golang:1.25-alpine go test ./internal/rbacstore/... -v ``` @@ -394,12 +394,12 @@ via `go.mod`'s `replace` directives to `../api`): ```sh docker run --rm --network sentry_default -v $(pwd)/..:/src -w /src/enterprise \ -e TENANTPROVISION_TEST_CLICKHOUSE_ADDR=clickhouse:9000 \ - -e TENANTPROVISION_TEST_CLICKHOUSE_PASSWORD=sentry-dev-only \ + -e TENANTPROVISION_TEST_CLICKHOUSE_PASSWORD=cairnobs-dev-only \ golang:1.25-alpine go test ./internal/tenantprovision/... -v docker run --rm --network sentry_default -v $(pwd)/..:/src -w /src/enterprise \ -e CHRUNNER_TEST_CLICKHOUSE_ADDR=clickhouse:9000 \ - -e CHRUNNER_TEST_CLICKHOUSE_PASSWORD=sentry-dev-only \ + -e CHRUNNER_TEST_CLICKHOUSE_PASSWORD=cairnobs-dev-only \ golang:1.25-alpine go test ./internal/chrunner/... -v ``` diff --git a/enterprise/cmd/enterprise-api/main.go b/enterprise/cmd/enterprise-api/main.go index 8d85d97..c1e3a75 100644 --- a/enterprise/cmd/enterprise-api/main.go +++ b/enterprise/cmd/enterprise-api/main.go @@ -42,23 +42,23 @@ import ( chdriver "github.com/ClickHouse/clickhouse-go/v2" "github.com/jackc/pgx/v5/pgxpool" - "github.com/sentry/sentry/api/agents" - "github.com/sentry/sentry/api/ai/aiapi" - "github.com/sentry/sentry/api/ai/provider/ollama" - "github.com/sentry/sentry/api/ai/router" - "github.com/sentry/sentry/api/authz" - "github.com/sentry/sentry/api/dashboards" - "github.com/sentry/sentry/api/httpserver" - "github.com/sentry/sentry/api/queryapi" + "github.com/cairnobs/cairnobs/api/agents" + "github.com/cairnobs/cairnobs/api/ai/aiapi" + "github.com/cairnobs/cairnobs/api/ai/provider/ollama" + "github.com/cairnobs/cairnobs/api/ai/router" + "github.com/cairnobs/cairnobs/api/authz" + "github.com/cairnobs/cairnobs/api/dashboards" + "github.com/cairnobs/cairnobs/api/httpserver" + "github.com/cairnobs/cairnobs/api/queryapi" - "github.com/sentry/sentry/enterprise/internal/apiconfig" - "github.com/sentry/sentry/enterprise/internal/audit" - "github.com/sentry/sentry/enterprise/internal/chrunner" - "github.com/sentry/sentry/enterprise/internal/groundingregistry" - "github.com/sentry/sentry/enterprise/internal/rbacstore" - "github.com/sentry/sentry/enterprise/internal/searchclient" - "github.com/sentry/sentry/enterprise/internal/tenantcrd" - "github.com/sentry/sentry/enterprise/internal/tenantprovision" + "github.com/cairnobs/cairnobs/enterprise/internal/apiconfig" + "github.com/cairnobs/cairnobs/enterprise/internal/audit" + "github.com/cairnobs/cairnobs/enterprise/internal/chrunner" + "github.com/cairnobs/cairnobs/enterprise/internal/groundingregistry" + "github.com/cairnobs/cairnobs/enterprise/internal/rbacstore" + "github.com/cairnobs/cairnobs/enterprise/internal/searchclient" + "github.com/cairnobs/cairnobs/enterprise/internal/tenantcrd" + "github.com/cairnobs/cairnobs/enterprise/internal/tenantprovision" ) // groundingRefreshInterval matches api/cmd/api's own constant of the @@ -295,7 +295,7 @@ func runProvisionTenant(ctx context.Context, logger *slog.Logger, cfg apiconfig. logger.Error("tenant is active but has no data source row -- inconsistent state, refusing", "tenant_id", tenantID) return 1 } - dataSource, err = rbac.CreateDataSource(ctx, tenantID, "default", tenantID, "/var/lib/sentry-search/tenants/"+tenantID) + dataSource, err = rbac.CreateDataSource(ctx, tenantID, "default", tenantID, "/var/lib/cairnobs-search/tenants/"+tenantID) if err != nil { logger.Error("creating data source", "error", err) return 1 diff --git a/enterprise/cmd/enterprise-auth/main.go b/enterprise/cmd/enterprise-auth/main.go index 574a40a..a7718fe 100644 --- a/enterprise/cmd/enterprise-auth/main.go +++ b/enterprise/cmd/enterprise-auth/main.go @@ -1,4 +1,4 @@ -// Command enterprise-auth is Sentry's SSO/tenant-provisioning/RBAC +// Command enterprise-auth is Cairn OBS's SSO/tenant-provisioning/RBAC // service (AGPLv3, same as core -- see // /docs/compliance/license-audit-report.md) -- see // /docs/phase-4-isolation-design.md and /docs/phase-4-rbac-design.md. @@ -38,14 +38,14 @@ import ( "github.com/crewjam/saml/samlsp" "github.com/jackc/pgx/v5/pgxpool" - "github.com/sentry/sentry/api/httpserver" - "github.com/sentry/sentry/enterprise/internal/authhandler" - "github.com/sentry/sentry/enterprise/internal/config" - "github.com/sentry/sentry/enterprise/internal/loginhandler" - "github.com/sentry/sentry/enterprise/internal/oidc" - "github.com/sentry/sentry/enterprise/internal/rbacstore" - samlpkg "github.com/sentry/sentry/enterprise/internal/saml" - "github.com/sentry/sentry/enterprise/internal/session" + "github.com/cairnobs/cairnobs/api/httpserver" + "github.com/cairnobs/cairnobs/enterprise/internal/authhandler" + "github.com/cairnobs/cairnobs/enterprise/internal/config" + "github.com/cairnobs/cairnobs/enterprise/internal/loginhandler" + "github.com/cairnobs/cairnobs/enterprise/internal/oidc" + "github.com/cairnobs/cairnobs/enterprise/internal/rbacstore" + samlpkg "github.com/cairnobs/cairnobs/enterprise/internal/saml" + "github.com/cairnobs/cairnobs/enterprise/internal/session" ) func main() { diff --git a/enterprise/cmd/enterprise-ingest/main.go b/enterprise/cmd/enterprise-ingest/main.go index fd38fb3..57c42b1 100644 --- a/enterprise/cmd/enterprise-ingest/main.go +++ b/enterprise/cmd/enterprise-ingest/main.go @@ -39,10 +39,10 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "golang.org/x/sync/errgroup" - "github.com/sentry/sentry/enterprise/internal/chwriter" - "github.com/sentry/sentry/enterprise/internal/ingestconfig" - "github.com/sentry/sentry/enterprise/internal/rbacstore" - "github.com/sentry/sentry/ingest/consumer" + "github.com/cairnobs/cairnobs/enterprise/internal/chwriter" + "github.com/cairnobs/cairnobs/enterprise/internal/ingestconfig" + "github.com/cairnobs/cairnobs/enterprise/internal/rbacstore" + "github.com/cairnobs/cairnobs/ingest/consumer" ) // dataSourceRefreshInterval matches search/src/tenants.rs's diff --git a/enterprise/go.mod b/enterprise/go.mod index 1f1ca97..162f6a4 100644 --- a/enterprise/go.mod +++ b/enterprise/go.mod @@ -1,4 +1,4 @@ -module github.com/sentry/sentry/enterprise +module github.com/cairnobs/cairnobs/enterprise go 1.25.0 @@ -7,24 +7,24 @@ go 1.25.0 // enterprise/internal/chrunner's doc comment: it implements api's // executor.SQLRunner interface, which structurally requires importing // the package that defines it. -replace github.com/sentry/sentry/api => ../api +replace github.com/cairnobs/cairnobs/api => ../api // Same allowed direction, against ingest/ instead -- enterprise/internal/ // chwriter implements ingest/consumer's chWriter interface, which // structurally requires importing the package that defines it (see // that package's doc comment). -replace github.com/sentry/sentry/ingest => ../ingest +replace github.com/cairnobs/cairnobs/ingest => ../ingest // api's own go.mod replace directive for proto/ is module-local and // doesn't propagate here -- enterprise/ needs its own, or `go build` -// tries to fetch github.com/sentry/sentry/proto from a real (nonexistent) +// tries to fetch github.com/cairnobs/cairnobs/proto from a real (nonexistent) // remote, since api/searchclient (now transitively imported) depends on // the generated search gRPC stubs. -replace github.com/sentry/sentry/proto => ../proto +replace github.com/cairnobs/cairnobs/proto => ../proto require ( - github.com/sentry/sentry/api v0.0.0-00010101000000-000000000000 - github.com/sentry/sentry/ingest v0.0.0-00010101000000-000000000000 + github.com/cairnobs/cairnobs/api v0.0.0-00010101000000-000000000000 + github.com/cairnobs/cairnobs/ingest v0.0.0-00010101000000-000000000000 ) require ( @@ -34,7 +34,7 @@ require ( github.com/go-jose/go-jose/v4 v4.1.4 github.com/google/uuid v1.6.0 github.com/jackc/pgx/v5 v5.10.0 - github.com/sentry/sentry/proto v0.0.0-00010101000000-000000000000 + github.com/cairnobs/cairnobs/proto v0.0.0-00010101000000-000000000000 golang.org/x/oauth2 v0.36.0 golang.org/x/sync v0.22.0 google.golang.org/grpc v1.83.0 diff --git a/enterprise/internal/apiconfig/apiconfig.go b/enterprise/internal/apiconfig/apiconfig.go index c969590..38c498f 100644 --- a/enterprise/internal/apiconfig/apiconfig.go +++ b/enterprise/internal/apiconfig/apiconfig.go @@ -92,7 +92,7 @@ type AuditWriterConfig struct { // DevCredentialWarnings for the full reasoning (duplicated here per // this repo's no-shared-code-between-services convention). const ( - devOnlyCredential = "sentry-dev-only" + devOnlyCredential = "cairnobs-dev-only" devOnlyAuditWriterCredential = "audit-writer-dev-only" ) diff --git a/enterprise/internal/audit/agent_command_adapter.go b/enterprise/internal/audit/agent_command_adapter.go index fe74f8c..ca5c962 100644 --- a/enterprise/internal/audit/agent_command_adapter.go +++ b/enterprise/internal/audit/agent_command_adapter.go @@ -8,8 +8,8 @@ import ( "encoding/json" "fmt" - "github.com/sentry/sentry/api/agents" - "github.com/sentry/sentry/api/authz" + "github.com/cairnobs/cairnobs/api/agents" + "github.com/cairnobs/cairnobs/api/authz" ) // AgentCommandLogger implements agents.CommandLogger by translating its diff --git a/enterprise/internal/audit/ai_interaction_adapter.go b/enterprise/internal/audit/ai_interaction_adapter.go index 7c72c75..2562147 100644 --- a/enterprise/internal/audit/ai_interaction_adapter.go +++ b/enterprise/internal/audit/ai_interaction_adapter.go @@ -8,8 +8,8 @@ import ( "encoding/json" "fmt" - "github.com/sentry/sentry/api/ai/aiapi" - "github.com/sentry/sentry/api/authz" + "github.com/cairnobs/cairnobs/api/ai/aiapi" + "github.com/cairnobs/cairnobs/api/authz" ) // AIInteractionLogger implements aiapi.InteractionLogger by translating diff --git a/enterprise/internal/audit/integration_test.go b/enterprise/internal/audit/integration_test.go index aa9d8f5..7982d59 100644 --- a/enterprise/internal/audit/integration_test.go +++ b/enterprise/internal/audit/integration_test.go @@ -7,7 +7,7 @@ // docker run --rm --network sentry_default -v $(pwd)/../../..:/src -w /src/enterprise \ // -e AUDIT_TEST_POSTGRES_ADDR=metadata-postgres:5432 \ // -e AUDIT_TEST_POSTGRES_PASSWORD=audit-writer-dev-only \ -// -e AUDIT_TEST_ADMIN_PASSWORD=sentry-dev-only \ +// -e AUDIT_TEST_ADMIN_PASSWORD=cairnobs-dev-only \ // golang:1.25-alpine go test ./internal/audit/... -v package audit @@ -23,9 +23,9 @@ import ( "github.com/jackc/pgx/v5/pgxpool" - "github.com/sentry/sentry/api/ai/aiapi" - "github.com/sentry/sentry/api/authz" - "github.com/sentry/sentry/api/queryapi" + "github.com/cairnobs/cairnobs/api/ai/aiapi" + "github.com/cairnobs/cairnobs/api/authz" + "github.com/cairnobs/cairnobs/api/queryapi" ) func testPool(t *testing.T, user, password string) *pgxpool.Pool { diff --git a/enterprise/internal/audit/queryapi_adapter.go b/enterprise/internal/audit/queryapi_adapter.go index d3b1e95..d75f517 100644 --- a/enterprise/internal/audit/queryapi_adapter.go +++ b/enterprise/internal/audit/queryapi_adapter.go @@ -10,8 +10,8 @@ import ( "context" "fmt" - "github.com/sentry/sentry/api/authz" - "github.com/sentry/sentry/api/queryapi" + "github.com/cairnobs/cairnobs/api/authz" + "github.com/cairnobs/cairnobs/api/queryapi" ) // QueryAPILogger implements queryapi.AuditLogger by translating its diff --git a/enterprise/internal/authhandler/authhandler.go b/enterprise/internal/authhandler/authhandler.go index 57f54b3..f66237a 100644 --- a/enterprise/internal/authhandler/authhandler.go +++ b/enterprise/internal/authhandler/authhandler.go @@ -25,12 +25,12 @@ import ( "net/http" "strings" - "github.com/sentry/sentry/enterprise/internal/session" + "github.com/cairnobs/cairnobs/enterprise/internal/session" ) // SessionCookieName matches the name api/authz.HTTPAuthorizer's -// tests and doc comments already assume ("sentry_session"). -const SessionCookieName = "sentry_session" +// tests and doc comments already assume ("cairnobs_session"). +const SessionCookieName = "cairnobs_session" // Features reports which SSO mechanisms are configured -- the response // shape /docs/phase-4-rbac-design.md's "Web UI boundary" section commits diff --git a/enterprise/internal/authhandler/authhandler_test.go b/enterprise/internal/authhandler/authhandler_test.go index 146f92e..d4d98aa 100644 --- a/enterprise/internal/authhandler/authhandler_test.go +++ b/enterprise/internal/authhandler/authhandler_test.go @@ -9,7 +9,7 @@ import ( "net/http/httptest" "testing" - "github.com/sentry/sentry/enterprise/internal/session" + "github.com/cairnobs/cairnobs/enterprise/internal/session" ) // fakeIngestCredentialValidator is an in-memory stand-in for diff --git a/enterprise/internal/chrunner/chrunner.go b/enterprise/internal/chrunner/chrunner.go index c00e995..613bcb3 100644 --- a/enterprise/internal/chrunner/chrunner.go +++ b/enterprise/internal/chrunner/chrunner.go @@ -29,8 +29,8 @@ import ( "fmt" "github.com/ClickHouse/clickhouse-go/v2" - "github.com/sentry/sentry/api/authz" - "github.com/sentry/sentry/api/querylang/executor" + "github.com/cairnobs/cairnobs/api/authz" + "github.com/cairnobs/cairnobs/api/querylang/executor" ) // DataSource is the minimal shape Registry needs to open one tenant's diff --git a/enterprise/internal/chrunner/chrunner_test.go b/enterprise/internal/chrunner/chrunner_test.go index d57603d..3347656 100644 --- a/enterprise/internal/chrunner/chrunner_test.go +++ b/enterprise/internal/chrunner/chrunner_test.go @@ -13,7 +13,7 @@ // // docker run --rm --network sentry_default -v $(pwd)/../../..:/src -w /src/enterprise \ // -e CHRUNNER_TEST_CLICKHOUSE_ADDR=clickhouse:9000 \ -// -e CHRUNNER_TEST_CLICKHOUSE_PASSWORD=sentry-dev-only \ +// -e CHRUNNER_TEST_CLICKHOUSE_PASSWORD=cairnobs-dev-only \ // golang:1.25-alpine go test ./internal/chrunner/... -v package chrunner @@ -25,9 +25,9 @@ import ( chdriver "github.com/ClickHouse/clickhouse-go/v2" "github.com/google/uuid" - "github.com/sentry/sentry/api/authz" + "github.com/cairnobs/cairnobs/api/authz" - "github.com/sentry/sentry/enterprise/internal/tenantprovision" + "github.com/cairnobs/cairnobs/enterprise/internal/tenantprovision" ) func testAddr(t *testing.T) string { diff --git a/enterprise/internal/chwriter/chwriter.go b/enterprise/internal/chwriter/chwriter.go index 5779c1e..6678506 100644 --- a/enterprise/internal/chwriter/chwriter.go +++ b/enterprise/internal/chwriter/chwriter.go @@ -28,9 +28,9 @@ import ( "sync" "time" - "github.com/sentry/sentry/ingest/clickhousewriter" - "github.com/sentry/sentry/ingest/consumer" - logsv1 "github.com/sentry/sentry/proto/sentry/logs/v1" + "github.com/cairnobs/cairnobs/ingest/clickhousewriter" + "github.com/cairnobs/cairnobs/ingest/consumer" + logsv1 "github.com/cairnobs/cairnobs/proto/sentry/logs/v1" ) // DataSource mirrors chrunner.DataSource -- deliberately not diff --git a/enterprise/internal/chwriter/chwriter_test.go b/enterprise/internal/chwriter/chwriter_test.go index 1976ddd..25f908e 100644 --- a/enterprise/internal/chwriter/chwriter_test.go +++ b/enterprise/internal/chwriter/chwriter_test.go @@ -8,7 +8,7 @@ // // docker run --rm --network sentry_default -v $(pwd)/../../..:/src -w /src/enterprise \ // -e CHWRITER_TEST_CLICKHOUSE_ADDR=clickhouse:9000 \ -// -e CHWRITER_TEST_CLICKHOUSE_PASSWORD=sentry-dev-only \ +// -e CHWRITER_TEST_CLICKHOUSE_PASSWORD=cairnobs-dev-only \ // golang:1.25-alpine go test ./internal/chwriter/... -v package chwriter @@ -24,10 +24,10 @@ import ( chdriver "github.com/ClickHouse/clickhouse-go/v2" "github.com/google/uuid" - "github.com/sentry/sentry/enterprise/internal/tenantprovision" - "github.com/sentry/sentry/ingest/clickhousewriter" - "github.com/sentry/sentry/ingest/consumer" - logsv1 "github.com/sentry/sentry/proto/sentry/logs/v1" + "github.com/cairnobs/cairnobs/enterprise/internal/tenantprovision" + "github.com/cairnobs/cairnobs/ingest/clickhousewriter" + "github.com/cairnobs/cairnobs/ingest/consumer" + logsv1 "github.com/cairnobs/cairnobs/proto/sentry/logs/v1" ) func discardLogger() *slog.Logger { diff --git a/enterprise/internal/config/config.go b/enterprise/internal/config/config.go index 3e84f02..afe4ae1 100644 --- a/enterprise/internal/config/config.go +++ b/enterprise/internal/config/config.go @@ -47,8 +47,8 @@ type Config struct { // in the enterprise deployment," since compromising it lets an attacker // forge any identity, including the RoleService credential. const ( - devOnlyCredential = "sentry-dev-only" - devOnlySigningKey = "sentry-dev-only-session-signing-key-32bytes+" + devOnlyCredential = "cairnobs-dev-only" + devOnlySigningKey = "cairnobs-dev-only-session-signing-key-32bytes+" ) // DevCredentialWarnings reports which configured secrets still equal diff --git a/enterprise/internal/groundingregistry/registry.go b/enterprise/internal/groundingregistry/registry.go index d8090ac..4ee2f56 100644 --- a/enterprise/internal/groundingregistry/registry.go +++ b/enterprise/internal/groundingregistry/registry.go @@ -26,10 +26,10 @@ import ( "sync" "time" - "github.com/sentry/sentry/api/ai/grounding" - "github.com/sentry/sentry/api/ai/provider" - "github.com/sentry/sentry/api/authz" - "github.com/sentry/sentry/api/querylang/executor" + "github.com/cairnobs/cairnobs/api/ai/grounding" + "github.com/cairnobs/cairnobs/api/ai/provider" + "github.com/cairnobs/cairnobs/api/authz" + "github.com/cairnobs/cairnobs/api/querylang/executor" ) // TenantLister returns the currently-active tenant IDs to sample -- diff --git a/enterprise/internal/groundingregistry/registry_test.go b/enterprise/internal/groundingregistry/registry_test.go index 04d14f1..9d85f0d 100644 --- a/enterprise/internal/groundingregistry/registry_test.go +++ b/enterprise/internal/groundingregistry/registry_test.go @@ -4,8 +4,8 @@ import ( "context" "testing" - "github.com/sentry/sentry/api/authz" - "github.com/sentry/sentry/api/querylang/executor" + "github.com/cairnobs/cairnobs/api/authz" + "github.com/cairnobs/cairnobs/api/querylang/executor" ) // tenantAwareFakeRunner returns a service list keyed by the tenant diff --git a/enterprise/internal/ingestconfig/ingestconfig.go b/enterprise/internal/ingestconfig/ingestconfig.go index 059f9a8..8bea8da 100644 --- a/enterprise/internal/ingestconfig/ingestconfig.go +++ b/enterprise/internal/ingestconfig/ingestconfig.go @@ -54,7 +54,7 @@ type BatchConfig struct { // api/internal/config.Config.DevCredentialWarnings for the full // reasoning (duplicated here per this repo's no-shared-code-between- // services convention). -const devOnlyCredential = "sentry-dev-only" +const devOnlyCredential = "cairnobs-dev-only" // DevCredentialWarnings reports whether the configured Postgres // credential still equals the literal dev-only default -- diff --git a/enterprise/internal/loginhandler/loginhandler.go b/enterprise/internal/loginhandler/loginhandler.go index e147ab8..2cadd30 100644 --- a/enterprise/internal/loginhandler/loginhandler.go +++ b/enterprise/internal/loginhandler/loginhandler.go @@ -32,11 +32,11 @@ import ( "net/http" "time" - "github.com/sentry/sentry/enterprise/internal/authhandler" - "github.com/sentry/sentry/enterprise/internal/oidc" - "github.com/sentry/sentry/enterprise/internal/rbacstore" - "github.com/sentry/sentry/enterprise/internal/saml" - "github.com/sentry/sentry/enterprise/internal/session" + "github.com/cairnobs/cairnobs/enterprise/internal/authhandler" + "github.com/cairnobs/cairnobs/enterprise/internal/oidc" + "github.com/cairnobs/cairnobs/enterprise/internal/rbacstore" + "github.com/cairnobs/cairnobs/enterprise/internal/saml" + "github.com/cairnobs/cairnobs/enterprise/internal/session" ) // oidcStateCookieName carries OIDC's CSRF-protection state value between @@ -44,19 +44,19 @@ import ( // callback-path cookie (the "double-submit cookie" pattern) rather than // server-side state, since this service otherwise has no per-browser // session store to put it in before a session exists. -const oidcStateCookieName = "sentry_oidc_state" +const oidcStateCookieName = "cairnobs_oidc_state" // samlRequestCookieName is SAML's analog -- carries the AuthnRequest ID // LoginURL generated, so the ACS handler can pass it back to // ParseResponse's possibleRequestIDs (SAML's actual replay/unsolicited- // response defense -- see saml.ServiceProvider.LoginURL's doc comment). -const samlRequestCookieName = "sentry_saml_request" +const samlRequestCookieName = "cairnobs_saml_request" // pendingLoginCookieName carries a PendingLoginClaims token from // finishLogin's multi-membership branch through GET /auth/memberships // and POST /auth/select-tenant -- Path "/auth" (not "/") so it's never // sent on ordinary requests, only the two routes that need it. -const pendingLoginCookieName = "sentry_pending_login" +const pendingLoginCookieName = "cairnobs_pending_login" // loginCookieTTL bounds how long a user has to complete the IdP round // trip -- generous enough for a real login form, short enough that a diff --git a/enterprise/internal/loginhandler/loginhandler_test.go b/enterprise/internal/loginhandler/loginhandler_test.go index 9ef45df..62525bb 100644 --- a/enterprise/internal/loginhandler/loginhandler_test.go +++ b/enterprise/internal/loginhandler/loginhandler_test.go @@ -23,12 +23,12 @@ import ( "github.com/coreos/go-oidc/v3/oidc/oidctest" - "github.com/sentry/sentry/enterprise/internal/oidc" - "github.com/sentry/sentry/enterprise/internal/rbacstore" - "github.com/sentry/sentry/enterprise/internal/session" + "github.com/cairnobs/cairnobs/enterprise/internal/oidc" + "github.com/cairnobs/cairnobs/enterprise/internal/rbacstore" + "github.com/cairnobs/cairnobs/enterprise/internal/session" ) -const testClientID = "sentry-test-client" +const testClientID = "cairnobs-test-client" const testKeyID = "test-key-1" // fakeUserStore is an in-memory stand-in for *rbacstore.Store, keyed by @@ -128,7 +128,7 @@ func newTestOIDCProvider(t *testing.T, idp *testIdP) *oidc.Provider { t.Helper() p, err := oidc.New(context.Background(), oidc.Config{ IssuerURL: idp.srv.URL, ClientID: testClientID, ClientSecret: "secret", - RedirectURL: "http://sentry-test/auth/oidc/callback", + RedirectURL: "http://cairnobs-test/auth/oidc/callback", }) if err != nil { t.Fatalf("oidc.New: %v", err) @@ -221,12 +221,12 @@ func TestFullLoginFlowIssuesSessionForSingleMembership(t *testing.T) { var sessionCookie *http.Cookie for _, c := range rec.Result().Cookies() { - if c.Name == "sentry_session" { + if c.Name == "cairnobs_session" { sessionCookie = c } } if sessionCookie == nil || sessionCookie.Value == "" { - t.Fatal("expected a sentry_session cookie to be set") + t.Fatal("expected a cairnobs_session cookie to be set") } claims, err := sessionManager.Validate(sessionCookie.Value) if err != nil { @@ -278,7 +278,7 @@ func TestFullLoginFlowStartsTenantSelectionForMultipleMemberships(t *testing.T) switch c.Name { case pendingLoginCookieName: pendingCookie = c - case "sentry_session": + case "cairnobs_session": sessionCookie = c } } @@ -408,14 +408,14 @@ func TestSelectTenantIssuesSessionForChosenTenant(t *testing.T) { var sessionCookie, clearedPendingCookie *http.Cookie for _, c := range rec.Result().Cookies() { switch c.Name { - case "sentry_session": + case "cairnobs_session": sessionCookie = c case pendingLoginCookieName: clearedPendingCookie = c } } if sessionCookie == nil || sessionCookie.Value == "" { - t.Fatal("expected a sentry_session cookie to be set") + t.Fatal("expected a cairnobs_session cookie to be set") } if clearedPendingCookie == nil || clearedPendingCookie.MaxAge >= 0 { t.Fatalf("expected the pending-login cookie to be cleared (MaxAge < 0), got %+v", clearedPendingCookie) @@ -458,7 +458,7 @@ func TestSelectTenantRejectsTenantOutsideMembership(t *testing.T) { t.Fatalf("status = %d, want 403; body=%s", rec.Code, rec.Body.String()) } for _, c := range rec.Result().Cookies() { - if c.Name == "sentry_session" { + if c.Name == "cairnobs_session" { t.Fatal("must not issue a session cookie for a tenant outside the identity's memberships") } } diff --git a/enterprise/internal/loginhandler/saml_test.go b/enterprise/internal/loginhandler/saml_test.go index badfaf5..c0d96cd 100644 --- a/enterprise/internal/loginhandler/saml_test.go +++ b/enterprise/internal/loginhandler/saml_test.go @@ -32,13 +32,13 @@ import ( "github.com/crewjam/saml" "github.com/crewjam/saml/samlidp" - "github.com/sentry/sentry/enterprise/internal/rbacstore" - samlpkg "github.com/sentry/sentry/enterprise/internal/saml" + "github.com/cairnobs/cairnobs/enterprise/internal/rbacstore" + samlpkg "github.com/cairnobs/cairnobs/enterprise/internal/saml" ) const ( - testSAMLEntityID = "https://sentry-test.example.com/saml/metadata" - testSAMLACSURL = "https://sentry-test.example.com/auth/saml/acs" + testSAMLEntityID = "https://cairnobs-test.example.com/saml/metadata" + testSAMLACSURL = "https://cairnobs-test.example.com/auth/saml/acs" ) // testSAMLIdP bundles a real samlidp.Server with the SP key/cert it was @@ -79,14 +79,14 @@ func genSelfSignedCert(t *testing.T, commonName string) (*rsa.PrivateKey, *x509. return key, cert } -// newTestSAMLIdP starts a real samlidp.Server and registers Sentry's SP +// newTestSAMLIdP starts a real samlidp.Server and registers Cairn OBS's SP // metadata with it directly via the IdP's own PUT /services/{id} // endpoint -- the same mechanism a real IdP admin uses, not a shortcut // that reaches into samlidp's unexported state. func newTestSAMLIdP(t *testing.T) *testSAMLIdP { t.Helper() - idpKey, idpCert := genSelfSignedCert(t, "sentry-test-idp") - spKey, spCert := genSelfSignedCert(t, "sentry-test-sp") + idpKey, idpCert := genSelfSignedCert(t, "cairnobs-test-idp") + spKey, spCert := genSelfSignedCert(t, "cairnobs-test-sp") store := &samlidp.MemoryStore{} idpServer, err := samlidp.New(samlidp.Options{ @@ -122,7 +122,7 @@ func newTestSAMLIdP(t *testing.T) *testSAMLIdP { if err != nil { t.Fatalf("marshaling sp metadata: %v", err) } - putReq := httptest.NewRequest(http.MethodPut, "/services/sentry-test-sp", strings.NewReader(string(spMetadataXML))) + putReq := httptest.NewRequest(http.MethodPut, "/services/cairnobs-test-sp", strings.NewReader(string(spMetadataXML))) putRec := httptest.NewRecorder() idpServer.ServeHTTP(putRec, putReq) if putRec.Code != http.StatusNoContent { @@ -324,12 +324,12 @@ func TestFullSAMLLoginFlowIssuesSessionForSingleMembership(t *testing.T) { var sessionCookie *http.Cookie for _, c := range rec.Result().Cookies() { - if c.Name == "sentry_session" { + if c.Name == "cairnobs_session" { sessionCookie = c } } if sessionCookie == nil || sessionCookie.Value == "" { - t.Fatal("expected a sentry_session cookie to be set") + t.Fatal("expected a cairnobs_session cookie to be set") } claims, err := sessionManager.Validate(sessionCookie.Value) if err != nil { @@ -401,7 +401,7 @@ func TestSAMLACSRejectsMissingRequestCookie(t *testing.T) { mux := http.NewServeMux() h.RegisterRoutes(mux) - // No prior GET /auth/saml/login, so no sentry_saml_request cookie -- + // No prior GET /auth/saml/login, so no cairnobs_saml_request cookie -- // simulates an attacker POSTing a captured/forged response directly // at the ACS endpoint with no matching request state. form := url.Values{"SAMLResponse": {"irrelevant"}, "RelayState": {""}} diff --git a/enterprise/internal/oidc/oidc.go b/enterprise/internal/oidc/oidc.go index 60b6f6b..0585c53 100644 --- a/enterprise/internal/oidc/oidc.go +++ b/enterprise/internal/oidc/oidc.go @@ -35,7 +35,7 @@ type Provider struct { oauth2 oauth2.Config } -// Claims is the subset of ID token claims Sentry actually uses. Extend +// Claims is the subset of ID token claims Cairn OBS actually uses. Extend // deliberately, not by passing the raw claim map further up the stack -- // every field added here is a field internal/session has to decide how // to trust. diff --git a/enterprise/internal/oidc/oidc_test.go b/enterprise/internal/oidc/oidc_test.go index 4ba524e..0b2f7c8 100644 --- a/enterprise/internal/oidc/oidc_test.go +++ b/enterprise/internal/oidc/oidc_test.go @@ -47,7 +47,7 @@ func TestNewDiscoversRealIssuer(t *testing.T) { }) p, err := New(context.Background(), Config{ - IssuerURL: srv.URL, ClientID: "sentry", ClientSecret: "secret", RedirectURL: "http://localhost/callback", + IssuerURL: srv.URL, ClientID: "cairnobs", ClientSecret: "secret", RedirectURL: "http://localhost/callback", }) if err != nil { t.Fatalf("New: %v", err) diff --git a/enterprise/internal/rbacstore/dashboards_adapter.go b/enterprise/internal/rbacstore/dashboards_adapter.go index be7ab59..3001650 100644 --- a/enterprise/internal/rbacstore/dashboards_adapter.go +++ b/enterprise/internal/rbacstore/dashboards_adapter.go @@ -12,8 +12,8 @@ import ( "errors" "fmt" - "github.com/sentry/sentry/api/authz" - "github.com/sentry/sentry/api/dashboards" + "github.com/cairnobs/cairnobs/api/authz" + "github.com/cairnobs/cairnobs/api/dashboards" ) // DashboardPermissions implements dashboards.PermissionStore by diff --git a/enterprise/internal/rbacstore/rbacstore_test.go b/enterprise/internal/rbacstore/rbacstore_test.go index be27529..84a49d9 100644 --- a/enterprise/internal/rbacstore/rbacstore_test.go +++ b/enterprise/internal/rbacstore/rbacstore_test.go @@ -6,7 +6,7 @@ // // docker run --rm --network sentry_default -v $(pwd)/../../..:/src -w /src/enterprise \ // -e RBACSTORE_TEST_POSTGRES_ADDR=metadata-postgres:5432 \ -// -e RBACSTORE_TEST_POSTGRES_PASSWORD=sentry-dev-only \ +// -e RBACSTORE_TEST_POSTGRES_PASSWORD=cairnobs-dev-only \ // golang:1.25-alpine go test ./internal/rbacstore/... -v package rbacstore @@ -19,7 +19,7 @@ import ( "github.com/google/uuid" "github.com/jackc/pgx/v5/pgxpool" - "github.com/sentry/sentry/api/authz" + "github.com/cairnobs/cairnobs/api/authz" ) func testStore(t *testing.T) *Store { @@ -337,7 +337,7 @@ func TestCreateDataSourceThenSetCredentials(t *testing.T) { t.Fatalf("CreateTenant: %v", err) } - ds, err := s.CreateDataSource(ctx, tenantID, "default", tenantID, "/var/lib/sentry-search/tenants/"+tenantID) + ds, err := s.CreateDataSource(ctx, tenantID, "default", tenantID, "/var/lib/cairnobs-search/tenants/"+tenantID) if err != nil { t.Fatalf("CreateDataSource: %v", err) } diff --git a/enterprise/internal/saml/saml.go b/enterprise/internal/saml/saml.go index f6ec2e6..dce9034 100644 --- a/enterprise/internal/saml/saml.go +++ b/enterprise/internal/saml/saml.go @@ -1,7 +1,7 @@ // Package saml wires crewjam/saml into a small SP (service provider) // client: build the login redirect, and validate/parse an incoming // assertion. Deliberately not using crewjam's samlsp.Middleware, which -// owns its own session/cookie handling -- Sentry's session concept lives +// owns its own session/cookie handling -- Cairn OBS's session concept lives // in internal/session, one layer up, so this package only does the SAML // protocol mechanics (XML signing/parsing), per the explicit instruction // not to hand-roll that crypto. @@ -23,7 +23,7 @@ import ( ) type Config struct { - // EntityID identifies Sentry to the IdP, conventionally Sentry's own + // EntityID identifies Cairn OBS to the IdP, conventionally Cairn OBS's own // metadata URL. EntityID string // ACSURL is where the IdP redirects the browser back to with the @@ -109,7 +109,7 @@ func (s *ServiceProvider) LoginURL(relayState string) (redirectURL, requestID st return redirect.String(), req.ID, nil } -// Claims is the subset of an assertion Sentry uses -- same "extend +// Claims is the subset of an assertion Cairn OBS uses -- same "extend // deliberately" reasoning as oidc.Claims. type Claims struct { NameID string @@ -117,7 +117,7 @@ type Claims struct { } // ParseResponse validates an incoming SAML response (signature, issuer, -// audience, timing) and extracts the fields Sentry cares about. This is +// audience, timing) and extracts the fields Cairn OBS cares about. This is // the step that actually establishes trust -- crewjam/saml's // ParseResponse does the XML signature verification, not this package. func (s *ServiceProvider) ParseResponse(r *http.Request, possibleRequestIDs []string) (*Claims, error) { @@ -186,7 +186,7 @@ func selfSignedCert() (*tls.Certificate, error) { } template := x509.Certificate{ SerialNumber: serial, - Subject: pkix.Name{CommonName: "sentry-saml-sp-dev"}, + Subject: pkix.Name{CommonName: "cairnobs-saml-sp-dev"}, NotBefore: time.Now().Add(-time.Hour), NotAfter: time.Now().Add(24 * time.Hour * 365), KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, diff --git a/enterprise/internal/searchclient/searchclient.go b/enterprise/internal/searchclient/searchclient.go index 2721c10..b5d38fc 100644 --- a/enterprise/internal/searchclient/searchclient.go +++ b/enterprise/internal/searchclient/searchclient.go @@ -41,8 +41,8 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - "github.com/sentry/sentry/api/authz" - searchv1 "github.com/sentry/sentry/proto/sentry/search/v1" + "github.com/cairnobs/cairnobs/api/authz" + searchv1 "github.com/cairnobs/cairnobs/proto/sentry/search/v1" ) // TenantChecker answers "is this tenant allowed to search at all" -- diff --git a/enterprise/internal/searchclient/searchclient_test.go b/enterprise/internal/searchclient/searchclient_test.go index c9262cd..1ff3a87 100644 --- a/enterprise/internal/searchclient/searchclient_test.go +++ b/enterprise/internal/searchclient/searchclient_test.go @@ -14,8 +14,8 @@ import ( "google.golang.org/grpc" - "github.com/sentry/sentry/api/authz" - searchv1 "github.com/sentry/sentry/proto/sentry/search/v1" + "github.com/cairnobs/cairnobs/api/authz" + searchv1 "github.com/cairnobs/cairnobs/proto/sentry/search/v1" ) type fakeSearchServer struct { diff --git a/enterprise/internal/tenantcrd/tenantcrd.go b/enterprise/internal/tenantcrd/tenantcrd.go index abb7903..3782b3b 100644 --- a/enterprise/internal/tenantcrd/tenantcrd.go +++ b/enterprise/internal/tenantcrd/tenantcrd.go @@ -38,10 +38,10 @@ import ( "k8s.io/client-go/tools/clientcmd" ) -// tenantGVR identifies deploy/operator/config/crd/sentry.io_tenants.yaml's +// tenantGVR identifies deploy/operator/config/crd/cairnobs.io_tenants.yaml's // resource -- kept as a plain schema.GroupVersionResource (not the typed // package) for the reason this file's doc comment explains. -var tenantGVR = schema.GroupVersionResource{Group: "sentry.io", Version: "v1alpha1", Resource: "tenants"} +var tenantGVR = schema.GroupVersionResource{Group: "cairnobs.io", Version: "v1alpha1", Resource: "tenants"} // Syncer talks to the K8s API. Construction (New) is the only place // that can fail for "no cluster reachable" reasons -- Sync itself @@ -105,7 +105,7 @@ type Credentials struct { // runProvisionTenant) finds and updates the same Secret rather than // creating a second one. func SecretName(tenantID string) string { - return fmt.Sprintf("sentry-tenant-%s-clickhouse", tenantID) + return fmt.Sprintf("cairnobs-tenant-%s-clickhouse", tenantID) } // Sync upserts the Tenant object (creating it with spec.displayName if @@ -145,7 +145,7 @@ func (s *Syncer) upsertTenant(ctx context.Context, tenantID, displayName string) } obj := &unstructured.Unstructured{Object: map[string]interface{}{ - "apiVersion": "sentry.io/v1alpha1", + "apiVersion": "cairnobs.io/v1alpha1", "kind": "Tenant", "metadata": map[string]interface{}{ "name": tenantID, @@ -170,8 +170,8 @@ func (s *Syncer) upsertSecret(ctx context.Context, tenantID, secretName string, Name: secretName, Namespace: s.namespace, Labels: map[string]string{ - "app.kubernetes.io/managed-by": "sentry-enterprise-api", - "sentry.io/tenant": tenantID, + "app.kubernetes.io/managed-by": "cairnobs-enterprise-api", + "cairnobs.io/tenant": tenantID, }, // Owned by the Tenant object even though a different // process (this one, not the operator's controller) @@ -179,7 +179,7 @@ func (s *Syncer) upsertSecret(ctx context.Context, tenantID, secretName string, // OwnerReferences regardless of which actor set them, so // deleting the Tenant still cleans this Secret up. OwnerReferences: []metav1.OwnerReference{{ - APIVersion: "sentry.io/v1alpha1", + APIVersion: "cairnobs.io/v1alpha1", Kind: "Tenant", Name: tenantID, UID: tenantUID, diff --git a/enterprise/internal/tenantcrd/tenantcrd_test.go b/enterprise/internal/tenantcrd/tenantcrd_test.go index daba990..92fb48e 100644 --- a/enterprise/internal/tenantcrd/tenantcrd_test.go +++ b/enterprise/internal/tenantcrd/tenantcrd_test.go @@ -39,8 +39,8 @@ func getTenant(t *testing.T, s *Syncer, tenantID string) *unstructured.Unstructu } func TestSyncCreatesTenantObjectWithDisplayName(t *testing.T) { - s := newTestSyncer(t, "sentry") - err := s.Sync(context.Background(), "acme", "Acme Corp", "/var/lib/sentry-search/tenants/acme", Credentials{Username: "tenant_acme", Password: "secret-pw"}) + s := newTestSyncer(t, "cairnobs") + err := s.Sync(context.Background(), "acme", "Acme Corp", "/var/lib/cairnobs-search/tenants/acme", Credentials{Username: "tenant_acme", Password: "secret-pw"}) if err != nil { t.Fatalf("Sync: %v", err) } @@ -53,8 +53,8 @@ func TestSyncCreatesTenantObjectWithDisplayName(t *testing.T) { } func TestSyncSetsRealStatusFields(t *testing.T) { - s := newTestSyncer(t, "sentry") - err := s.Sync(context.Background(), "acme", "Acme Corp", "/var/lib/sentry-search/tenants/acme", Credentials{Username: "tenant_acme", Password: "secret-pw"}) + s := newTestSyncer(t, "cairnobs") + err := s.Sync(context.Background(), "acme", "Acme Corp", "/var/lib/cairnobs-search/tenants/acme", Credentials{Username: "tenant_acme", Password: "secret-pw"}) if err != nil { t.Fatalf("Sync: %v", err) } @@ -65,23 +65,23 @@ func TestSyncSetsRealStatusFields(t *testing.T) { t.Fatalf("status.clickHouseDatabaseName = %q, want acme", dbName) } secretRef, _, _ := unstructured.NestedString(obj.Object, "status", "clickHouseSecretRef") - if secretRef != "sentry-tenant-acme-clickhouse" { - t.Fatalf("status.clickHouseSecretRef = %q, want sentry-tenant-acme-clickhouse", secretRef) + if secretRef != "cairnobs-tenant-acme-clickhouse" { + t.Fatalf("status.clickHouseSecretRef = %q, want cairnobs-tenant-acme-clickhouse", secretRef) } indexPath, _, _ := unstructured.NestedString(obj.Object, "status", "tantivyIndexPath") - if indexPath != "/var/lib/sentry-search/tenants/acme" { - t.Fatalf("status.tantivyIndexPath = %q, want /var/lib/sentry-search/tenants/acme", indexPath) + if indexPath != "/var/lib/cairnobs-search/tenants/acme" { + t.Fatalf("status.tantivyIndexPath = %q, want /var/lib/cairnobs-search/tenants/acme", indexPath) } } func TestSyncCreatesSecretOwnedByTenant(t *testing.T) { - s := newTestSyncer(t, "sentry") + s := newTestSyncer(t, "cairnobs") err := s.Sync(context.Background(), "acme", "Acme Corp", "/idx", Credentials{Username: "tenant_acme", Password: "secret-pw"}) if err != nil { t.Fatalf("Sync: %v", err) } - secret, err := s.clientset.CoreV1().Secrets("sentry").Get(context.Background(), "sentry-tenant-acme-clickhouse", metav1.GetOptions{}) + secret, err := s.clientset.CoreV1().Secrets("cairnobs").Get(context.Background(), "cairnobs-tenant-acme-clickhouse", metav1.GetOptions{}) if err != nil { t.Fatalf("getting secret: %v", err) } @@ -100,7 +100,7 @@ func TestSyncCreatesSecretOwnedByTenant(t *testing.T) { // Tenant object, must not error, and must never silently swap in // different credentials than what was passed. func TestSyncIsIdempotentAndNeverChangesCredentials(t *testing.T) { - s := newTestSyncer(t, "sentry") + s := newTestSyncer(t, "cairnobs") ctx := context.Background() creds := Credentials{Username: "tenant_acme", Password: "secret-pw"} @@ -111,7 +111,7 @@ func TestSyncIsIdempotentAndNeverChangesCredentials(t *testing.T) { t.Fatalf("second Sync: %v", err) } - list, err := s.dynamic.Resource(tenantGVR).Namespace("sentry").List(ctx, metav1.ListOptions{}) + list, err := s.dynamic.Resource(tenantGVR).Namespace("cairnobs").List(ctx, metav1.ListOptions{}) if err != nil { t.Fatalf("listing tenants: %v", err) } @@ -119,7 +119,7 @@ func TestSyncIsIdempotentAndNeverChangesCredentials(t *testing.T) { t.Fatalf("expected exactly one Tenant object after two Syncs, got %d", len(list.Items)) } - secret, err := s.clientset.CoreV1().Secrets("sentry").Get(ctx, "sentry-tenant-acme-clickhouse", metav1.GetOptions{}) + secret, err := s.clientset.CoreV1().Secrets("cairnobs").Get(ctx, "cairnobs-tenant-acme-clickhouse", metav1.GetOptions{}) if err != nil { t.Fatalf("getting secret: %v", err) } @@ -129,7 +129,7 @@ func TestSyncIsIdempotentAndNeverChangesCredentials(t *testing.T) { } func TestSyncPreservesExistingTenantObjectDisplayName(t *testing.T) { - s := newTestSyncer(t, "sentry") + s := newTestSyncer(t, "cairnobs") ctx := context.Background() // A human/GitOps process already created this Tenant object (e.g. @@ -137,12 +137,12 @@ func TestSyncPreservesExistingTenantObjectDisplayName(t *testing.T) { // -provision-tenant ever ran -- Sync must not overwrite their // chosen displayName with its own. pre := &unstructured.Unstructured{Object: map[string]interface{}{ - "apiVersion": "sentry.io/v1alpha1", + "apiVersion": "cairnobs.io/v1alpha1", "kind": "Tenant", - "metadata": map[string]interface{}{"name": "acme", "namespace": "sentry"}, + "metadata": map[string]interface{}{"name": "acme", "namespace": "cairnobs"}, "spec": map[string]interface{}{"displayName": "Human-Chosen Name"}, }} - if _, err := s.dynamic.Resource(tenantGVR).Namespace("sentry").Create(ctx, pre, metav1.CreateOptions{}); err != nil { + if _, err := s.dynamic.Resource(tenantGVR).Namespace("cairnobs").Create(ctx, pre, metav1.CreateOptions{}); err != nil { t.Fatalf("pre-creating tenant: %v", err) } diff --git a/enterprise/internal/tenantprovision/tenantprovision_test.go b/enterprise/internal/tenantprovision/tenantprovision_test.go index 393e903..2896d11 100644 --- a/enterprise/internal/tenantprovision/tenantprovision_test.go +++ b/enterprise/internal/tenantprovision/tenantprovision_test.go @@ -5,7 +5,7 @@ // // docker run --rm --network sentry_default -v $(pwd)/../../..:/src -w /src/enterprise \ // -e TENANTPROVISION_TEST_CLICKHOUSE_ADDR=clickhouse:9000 \ -// -e TENANTPROVISION_TEST_CLICKHOUSE_PASSWORD=sentry-dev-only \ +// -e TENANTPROVISION_TEST_CLICKHOUSE_PASSWORD=cairnobs-dev-only \ // golang:1.25-alpine go test ./internal/tenantprovision/... -v package tenantprovision diff --git a/hack/alert-load-test/go.mod b/hack/alert-load-test/go.mod index 3a5b8f6..8c01485 100644 --- a/hack/alert-load-test/go.mod +++ b/hack/alert-load-test/go.mod @@ -1,3 +1,3 @@ -module github.com/sentry/sentry/hack/alert-load-test +module github.com/cairnobs/cairnobs/hack/alert-load-test go 1.25.0 diff --git a/hack/benchmark-fixture/go.mod b/hack/benchmark-fixture/go.mod index 125a6b0..f318930 100644 --- a/hack/benchmark-fixture/go.mod +++ b/hack/benchmark-fixture/go.mod @@ -1,11 +1,11 @@ -module github.com/sentry/sentry/hack/benchmark-fixture +module github.com/cairnobs/cairnobs/hack/benchmark-fixture go 1.25.0 -replace github.com/sentry/sentry/proto => ../../proto +replace github.com/cairnobs/cairnobs/proto => ../../proto require ( - github.com/sentry/sentry/proto v0.0.0-00010101000000-000000000000 + github.com/cairnobs/cairnobs/proto v0.0.0-00010101000000-000000000000 google.golang.org/grpc v1.83.0 ) diff --git a/hack/benchmark-fixture/main.go b/hack/benchmark-fixture/main.go index fd7a264..fcdef89 100644 --- a/hack/benchmark-fixture/main.go +++ b/hack/benchmark-fixture/main.go @@ -23,7 +23,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials" - logsv1 "github.com/sentry/sentry/proto/sentry/logs/v1" + logsv1 "github.com/cairnobs/cairnobs/proto/sentry/logs/v1" ) var ( diff --git a/hack/check-tenant-boundary.sh b/hack/check-tenant-boundary.sh index 6869103..6ec1677 100755 --- a/hack/check-tenant-boundary.sh +++ b/hack/check-tenant-boundary.sh @@ -27,7 +27,7 @@ echo "Checking: no core Go package imports enterprise/..." # tooling isn't shipped, and load-test/fixture scripts have no reason to # import enterprise/ either, but they're not part of the architectural # boundary claim, so they're excluded rather than asserted about). -core_hits="$(grep -rn '"github.com/sentry/sentry/enterprise' \ +core_hits="$(grep -rn '"github.com/cairnobs/cairnobs/enterprise' \ --include='*.go' \ agent ingest storage api web cli alerting 2>/dev/null || true)" if [[ -n "$core_hits" ]]; then diff --git a/hack/dev-certs/generate.sh b/hack/dev-certs/generate.sh index d5d594d..aada4fb 100755 --- a/hack/dev-certs/generate.sh +++ b/hack/dev-certs/generate.sh @@ -18,12 +18,12 @@ cd "${OUT_DIR}" echo "Generating dev CA..." openssl req -x509 -newkey rsa:4096 -sha256 -days "${DAYS}" -nodes \ -keyout ca-key.pem -out ca.pem \ - -subj "/O=Sentry Dev/CN=Sentry Dev CA" + -subj "/O=Cairn OBS Dev/CN=Cairn OBS Dev CA" gen_leaf() { local name="$1" cn="$2" san="$3" openssl req -newkey rsa:2048 -nodes -keyout "${name}-key.pem" -out "${name}.csr" \ - -subj "/O=Sentry Dev/CN=${cn}" + -subj "/O=Cairn OBS Dev/CN=${cn}" openssl x509 -req -in "${name}.csr" -CA ca.pem -CAkey ca-key.pem -CAcreateserial \ -out "${name}.pem" -days "${DAYS}" -sha256 \ -extfile <(printf "subjectAltName=%s" "${san}") @@ -37,7 +37,7 @@ echo "Generating server (ingest) cert..." gen_leaf server ingest "DNS:ingest,DNS:localhost,IP:127.0.0.1" echo "Generating client (agent) cert..." -gen_leaf client sentry-agent "DNS:sentry-agent" +gen_leaf client cairnobs-agent "DNS:cairnobs-agent" rm -f ca.srl diff --git a/hack/mock-ollama/go.mod b/hack/mock-ollama/go.mod index 49c82f1..6c6a0ad 100644 --- a/hack/mock-ollama/go.mod +++ b/hack/mock-ollama/go.mod @@ -1,3 +1,3 @@ -module github.com/sentry/sentry/hack/mock-ollama +module github.com/cairnobs/cairnobs/hack/mock-ollama go 1.25.0 diff --git a/hack/webhook-sink/go.mod b/hack/webhook-sink/go.mod index 356772d..371f54c 100644 --- a/hack/webhook-sink/go.mod +++ b/hack/webhook-sink/go.mod @@ -1,3 +1,3 @@ -module github.com/sentry/sentry/hack/webhook-sink +module github.com/cairnobs/cairnobs/hack/webhook-sink go 1.25.0 diff --git a/hack/windows-fixture/go.mod b/hack/windows-fixture/go.mod index 4cca78d..2494c28 100644 --- a/hack/windows-fixture/go.mod +++ b/hack/windows-fixture/go.mod @@ -1,11 +1,11 @@ -module github.com/sentry/sentry/hack/windows-fixture +module github.com/cairnobs/cairnobs/hack/windows-fixture go 1.25.0 -replace github.com/sentry/sentry/proto => ../../proto +replace github.com/cairnobs/cairnobs/proto => ../../proto require ( - github.com/sentry/sentry/proto v0.0.0-00010101000000-000000000000 + github.com/cairnobs/cairnobs/proto v0.0.0-00010101000000-000000000000 google.golang.org/grpc v1.83.0 ) diff --git a/hack/windows-fixture/main.go b/hack/windows-fixture/main.go index 41facdf..655b860 100644 --- a/hack/windows-fixture/main.go +++ b/hack/windows-fixture/main.go @@ -23,7 +23,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials" - logsv1 "github.com/sentry/sentry/proto/sentry/logs/v1" + logsv1 "github.com/cairnobs/cairnobs/proto/sentry/logs/v1" ) func main() { diff --git a/ingest/clickhousewriter/writer.go b/ingest/clickhousewriter/writer.go index f6ebd00..83f2fc9 100644 --- a/ingest/clickhousewriter/writer.go +++ b/ingest/clickhousewriter/writer.go @@ -17,8 +17,8 @@ import ( "github.com/ClickHouse/clickhouse-go/v2" "github.com/ClickHouse/clickhouse-go/v2/lib/driver" - "github.com/sentry/sentry/ingest/internal/normalize" - logsv1 "github.com/sentry/sentry/proto/sentry/logs/v1" + "github.com/cairnobs/cairnobs/ingest/internal/normalize" + logsv1 "github.com/cairnobs/cairnobs/proto/sentry/logs/v1" ) // Config is deliberately a local type, not ingest/internal/config. diff --git a/ingest/cmd/ingest/main.go b/ingest/cmd/ingest/main.go index 24596bb..4069924 100644 --- a/ingest/cmd/ingest/main.go +++ b/ingest/cmd/ingest/main.go @@ -22,14 +22,14 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "golang.org/x/sync/errgroup" - "github.com/sentry/sentry/ingest/clickhousewriter" - "github.com/sentry/sentry/ingest/consumer" - "github.com/sentry/sentry/ingest/internal/agentregistry" - "github.com/sentry/sentry/ingest/internal/config" - "github.com/sentry/sentry/ingest/internal/grpcserver" - "github.com/sentry/sentry/ingest/internal/producer" - "github.com/sentry/sentry/ingest/internal/tenantresolver" - logsv1 "github.com/sentry/sentry/proto/sentry/logs/v1" + "github.com/cairnobs/cairnobs/ingest/clickhousewriter" + "github.com/cairnobs/cairnobs/ingest/consumer" + "github.com/cairnobs/cairnobs/ingest/internal/agentregistry" + "github.com/cairnobs/cairnobs/ingest/internal/config" + "github.com/cairnobs/cairnobs/ingest/internal/grpcserver" + "github.com/cairnobs/cairnobs/ingest/internal/producer" + "github.com/cairnobs/cairnobs/ingest/internal/tenantresolver" + logsv1 "github.com/cairnobs/cairnobs/proto/sentry/logs/v1" ) // singleTenantWriter adapts *clickhousewriter.Writer -- which only diff --git a/ingest/cmd/ingest/main_test.go b/ingest/cmd/ingest/main_test.go index 1dddb87..5d2a581 100644 --- a/ingest/cmd/ingest/main_test.go +++ b/ingest/cmd/ingest/main_test.go @@ -3,8 +3,8 @@ package main import ( "testing" - "github.com/sentry/sentry/ingest/consumer" - "github.com/sentry/sentry/ingest/internal/grpcserver" + "github.com/cairnobs/cairnobs/ingest/consumer" + "github.com/cairnobs/cairnobs/ingest/internal/grpcserver" ) // TestTenantIDHeaderKeyConstantsMatch guards against the literal drift diff --git a/ingest/consumer/consumer.go b/ingest/consumer/consumer.go index e5fcafe..d2babf8 100644 --- a/ingest/consumer/consumer.go +++ b/ingest/consumer/consumer.go @@ -30,7 +30,7 @@ import ( "github.com/segmentio/kafka-go" "google.golang.org/protobuf/proto" - logsv1 "github.com/sentry/sentry/proto/sentry/logs/v1" + logsv1 "github.com/cairnobs/cairnobs/proto/sentry/logs/v1" ) // TenantIDHeaderKey mirrors ingest/internal/grpcserver.TenantIDHeaderKey diff --git a/ingest/consumer/consumer_test.go b/ingest/consumer/consumer_test.go index 35125ec..036f656 100644 --- a/ingest/consumer/consumer_test.go +++ b/ingest/consumer/consumer_test.go @@ -12,7 +12,7 @@ import ( "github.com/segmentio/kafka-go" "google.golang.org/protobuf/proto" - logsv1 "github.com/sentry/sentry/proto/sentry/logs/v1" + logsv1 "github.com/cairnobs/cairnobs/proto/sentry/logs/v1" ) type fakeReader struct { diff --git a/ingest/go.mod b/ingest/go.mod index db2f97a..dc484ec 100644 --- a/ingest/go.mod +++ b/ingest/go.mod @@ -1,14 +1,14 @@ -module github.com/sentry/sentry/ingest +module github.com/cairnobs/cairnobs/ingest go 1.25.0 -replace github.com/sentry/sentry/proto => ../proto +replace github.com/cairnobs/cairnobs/proto => ../proto require ( github.com/ClickHouse/clickhouse-go/v2 v2.48.0 github.com/google/uuid v1.6.0 github.com/segmentio/kafka-go v0.4.51 - github.com/sentry/sentry/proto v0.0.0-00010101000000-000000000000 + github.com/cairnobs/cairnobs/proto v0.0.0-00010101000000-000000000000 golang.org/x/sync v0.22.0 google.golang.org/grpc v1.83.0 google.golang.org/protobuf v1.36.12 diff --git a/ingest/internal/agentregistry/agentregistry.go b/ingest/internal/agentregistry/agentregistry.go index ac3c05b..cba25da 100644 --- a/ingest/internal/agentregistry/agentregistry.go +++ b/ingest/internal/agentregistry/agentregistry.go @@ -17,7 +17,7 @@ import ( "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" - "github.com/sentry/sentry/ingest/internal/grpcserver" + "github.com/cairnobs/cairnobs/ingest/internal/grpcserver" ) // defaultTenantID is used when no TenantResolver is configured (empty diff --git a/ingest/internal/config/config.go b/ingest/internal/config/config.go index 3e6e77e..b52ee27 100644 --- a/ingest/internal/config/config.go +++ b/ingest/internal/config/config.go @@ -80,7 +80,7 @@ type BatchConfig struct { // api/internal/config.Config.DevCredentialWarnings for the full // reasoning (duplicated here per this repo's no-shared-code-between- // services convention). -const devOnlyCredential = "sentry-dev-only" +const devOnlyCredential = "cairnobs-dev-only" // DevCredentialWarnings reports which configured credentials still // equal the literal dev-only default -- cmd/ingest/main.go logs each @@ -104,9 +104,9 @@ func Load() (Config, error) { ListenAddr: getenv("GRPC_LISTEN_ADDR", ":4317"), }, TLS: TLSConfig{ - CertFile: getenv("TLS_CERT_FILE", "/etc/sentry-ingest/server.pem"), - KeyFile: getenv("TLS_KEY_FILE", "/etc/sentry-ingest/server-key.pem"), - ClientCAFile: getenv("TLS_CLIENT_CA_FILE", "/etc/sentry-ingest/ca.pem"), + CertFile: getenv("TLS_CERT_FILE", "/etc/cairnobs-ingest/server.pem"), + KeyFile: getenv("TLS_KEY_FILE", "/etc/cairnobs-ingest/server-key.pem"), + ClientCAFile: getenv("TLS_CLIENT_CA_FILE", "/etc/cairnobs-ingest/ca.pem"), }, Redpanda: RedpandaConfig{ Brokers: strings.Split(getenv("REDPANDA_BROKERS", "localhost:9092"), ","), diff --git a/ingest/internal/grpcserver/server.go b/ingest/internal/grpcserver/server.go index be8b233..56d48e5 100644 --- a/ingest/internal/grpcserver/server.go +++ b/ingest/internal/grpcserver/server.go @@ -38,9 +38,9 @@ import ( "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" - "github.com/sentry/sentry/ingest/internal/config" - agentv1 "github.com/sentry/sentry/proto/sentry/agent/v1" - logsv1 "github.com/sentry/sentry/proto/sentry/logs/v1" + "github.com/cairnobs/cairnobs/ingest/internal/config" + agentv1 "github.com/cairnobs/cairnobs/proto/sentry/agent/v1" + logsv1 "github.com/cairnobs/cairnobs/proto/sentry/logs/v1" ) // TenantIDHeaderKey is the Kafka message header a resolved tenant ID is diff --git a/ingest/internal/grpcserver/server_test.go b/ingest/internal/grpcserver/server_test.go index 7232848..51f2afa 100644 --- a/ingest/internal/grpcserver/server_test.go +++ b/ingest/internal/grpcserver/server_test.go @@ -14,9 +14,9 @@ import ( "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" - "github.com/sentry/sentry/ingest/internal/config" - agentv1 "github.com/sentry/sentry/proto/sentry/agent/v1" - logsv1 "github.com/sentry/sentry/proto/sentry/logs/v1" + "github.com/cairnobs/cairnobs/ingest/internal/config" + agentv1 "github.com/cairnobs/cairnobs/proto/sentry/agent/v1" + logsv1 "github.com/cairnobs/cairnobs/proto/sentry/logs/v1" ) type fakeProducer struct { diff --git a/ingest/internal/grpcserver/tls.go b/ingest/internal/grpcserver/tls.go index 093ecea..e1f3542 100644 --- a/ingest/internal/grpcserver/tls.go +++ b/ingest/internal/grpcserver/tls.go @@ -6,7 +6,7 @@ import ( "fmt" "os" - "github.com/sentry/sentry/ingest/internal/config" + "github.com/cairnobs/cairnobs/ingest/internal/config" ) // loadServerTLSConfig builds the mTLS server config: ingest's own diff --git a/ingest/internal/normalize/normalize.go b/ingest/internal/normalize/normalize.go index 0e2a511..90ca307 100644 --- a/ingest/internal/normalize/normalize.go +++ b/ingest/internal/normalize/normalize.go @@ -11,7 +11,7 @@ import ( "github.com/google/uuid" - logsv1 "github.com/sentry/sentry/proto/sentry/logs/v1" + logsv1 "github.com/cairnobs/cairnobs/proto/sentry/logs/v1" ) type Row struct { diff --git a/ingest/internal/normalize/normalize_test.go b/ingest/internal/normalize/normalize_test.go index 4d9b718..cf57537 100644 --- a/ingest/internal/normalize/normalize_test.go +++ b/ingest/internal/normalize/normalize_test.go @@ -6,7 +6,7 @@ import ( "github.com/google/uuid" - logsv1 "github.com/sentry/sentry/proto/sentry/logs/v1" + logsv1 "github.com/cairnobs/cairnobs/proto/sentry/logs/v1" ) func TestToRowMapsFieldsAndSeverity(t *testing.T) { diff --git a/ingest/internal/producer/producer.go b/ingest/internal/producer/producer.go index 375e4b6..1c6eb17 100644 --- a/ingest/internal/producer/producer.go +++ b/ingest/internal/producer/producer.go @@ -9,7 +9,7 @@ import ( "github.com/segmentio/kafka-go" - "github.com/sentry/sentry/ingest/internal/config" + "github.com/cairnobs/cairnobs/ingest/internal/config" ) type Producer struct { diff --git a/metadata/README.md b/metadata/README.md index a8954c9..2a727cd 100644 --- a/metadata/README.md +++ b/metadata/README.md @@ -1,6 +1,6 @@ # metadata -PostgreSQL schema and migration tooling for Sentry's control-plane +PostgreSQL schema and migration tooling for Cairn OBS's control-plane config: dashboards, alert rules, and everything else that isn't log data. See `/docs/phase-3-dashboard-design.md` and `/docs/phase-3-alerting-design.md` for why this is a separate database @@ -78,7 +78,7 @@ unlike ClickHouse's HTTP interface). ```sh docker compose up -d # starts a standalone Postgres for local work -POSTGRES_PASSWORD=sentry-dev-only ./migrate.sh # applies migrations/*.sql +POSTGRES_PASSWORD=cairnobs-dev-only ./migrate.sh # applies migrations/*.sql ``` Environment variables `migrate.sh` reads (all optional except diff --git a/metadata/docker-compose.yml b/metadata/docker-compose.yml index 912d9e9..52eae6a 100644 --- a/metadata/docker-compose.yml +++ b/metadata/docker-compose.yml @@ -5,13 +5,13 @@ services: postgres: image: postgres:16-alpine - container_name: sentry-metadata-postgres + container_name: cairnobs-metadata-postgres ports: - "5432:5432" environment: POSTGRES_DB: sentry_metadata POSTGRES_USER: sentry - POSTGRES_PASSWORD: "sentry-dev-only" # not a real secret, see root docker-compose.yml + POSTGRES_PASSWORD: "cairnobs-dev-only" # not a real secret, see root docker-compose.yml volumes: - metadata-postgres-data:/var/lib/postgresql/data diff --git a/proto/README.md b/proto/README.md index 25cb094..5437062 100644 --- a/proto/README.md +++ b/proto/README.md @@ -17,10 +17,10 @@ than sharing generated code across languages. Go is the one language here with pre-generated, checked-in bindings (`sentry/logs/v1/logs.pb.go`, `logs_grpc.pb.go`), living in this directory -as its own module (`github.com/sentry/sentry/proto`) that `/ingest` and +as its own module (`github.com/cairnobs/cairnobs/proto`) that `/ingest` and `/api` depend on via a local `replace` directive in their `go.mod`. Rust (`/agent`) instead generates its bindings at build time via `tonic-build` -(see `agent/sentry-agent/build.rs`) — no checked-in Rust output. +(see `agent/cairnobs-agent/build.rs`) — no checked-in Rust output. To regenerate the Go bindings after changing either `.proto` file: diff --git a/proto/go.mod b/proto/go.mod index 2aac19a..80fabb7 100644 --- a/proto/go.mod +++ b/proto/go.mod @@ -1,4 +1,4 @@ -module github.com/sentry/sentry/proto +module github.com/cairnobs/cairnobs/proto go 1.25.0 diff --git a/proto/sentry/agent/v1/agent_control.pb.go b/proto/sentry/agent/v1/agent_control.pb.go index 86a862e..4f92dd5 100644 --- a/proto/sentry/agent/v1/agent_control.pb.go +++ b/proto/sentry/agent/v1/agent_control.pb.go @@ -482,7 +482,7 @@ const file_sentry_agent_v1_agent_control_proto_rawDesc = "" + "\x19AGENT_COMMAND_UNSPECIFIED\x10\x00\x12\x19\n" + "\x15AGENT_COMMAND_RESTART\x10\x012\\\n" + "\fAgentControl\x12L\n" + - "\aCheckIn\x12\x1f.sentry.agent.v1.CheckInRequest\x1a .sentry.agent.v1.CheckInResponseB8Z6github.com/sentry/sentry/proto/sentry/agent/v1;agentv1b\x06proto3" + "\aCheckIn\x12\x1f.sentry.agent.v1.CheckInRequest\x1a .sentry.agent.v1.CheckInResponseBZ sentry.search.v1.SearchRequest - 1, // 1: sentry.search.v1.SearchService.Search:output_type -> sentry.search.v1.SearchResponse + 0, // 0: cairnobs.search.v1.SearchService.Search:input_type -> cairnobs.search.v1.SearchRequest + 1, // 1: cairnobs.search.v1.SearchService.Search:output_type -> cairnobs.search.v1.SearchResponse 1, // [1:2] is the sub-list for method output_type 0, // [0:1] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name diff --git a/proto/sentry/search/v1/search.proto b/proto/sentry/search/v1/search.proto index 5a753d3..9167a3d 100644 --- a/proto/sentry/search/v1/search.proto +++ b/proto/sentry/search/v1/search.proto @@ -1,8 +1,8 @@ syntax = "proto3"; -package sentry.search.v1; +package cairnobs.search.v1; -option go_package = "github.com/sentry/sentry/proto/sentry/search/v1;searchv1"; +option go_package = "github.com/cairnobs/cairnobs/proto/sentry/search/v1;searchv1"; // SearchService is the full-text search index (Tantivy-backed) that // `api` calls to resolve a free-text query into matching record_ids, diff --git a/proto/sentry/search/v1/search_grpc.pb.go b/proto/sentry/search/v1/search_grpc.pb.go index 9eab644..8e4079a 100644 --- a/proto/sentry/search/v1/search_grpc.pb.go +++ b/proto/sentry/search/v1/search_grpc.pb.go @@ -19,7 +19,7 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( - SearchService_Search_FullMethodName = "/sentry.search.v1.SearchService/Search" + SearchService_Search_FullMethodName = "/cairnobs.search.v1.SearchService/Search" ) // SearchServiceClient is the client API for SearchService service. @@ -120,7 +120,7 @@ func _SearchService_Search_Handler(srv interface{}, ctx context.Context, dec fun // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var SearchService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "sentry.search.v1.SearchService", + ServiceName: "cairnobs.search.v1.SearchService", HandlerType: (*SearchServiceServer)(nil), Methods: []grpc.MethodDesc{ { diff --git a/search/Cargo.lock b/search/Cargo.lock index fd77888..1744d9f 100644 --- a/search/Cargo.lock +++ b/search/Cargo.lock @@ -178,6 +178,25 @@ version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" +[[package]] +name = "cairnobs-search" +version = "0.1.0" +dependencies = [ + "anyhow", + "prost", + "reqwest", + "rskafka", + "serde", + "serde_json", + "tantivy", + "tempfile", + "tokio", + "tonic", + "tonic-build", + "tracing", + "tracing-subscriber", +] + [[package]] name = "cc" version = "1.4.2" @@ -1575,25 +1594,6 @@ version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" -[[package]] -name = "sentry-search" -version = "0.1.0" -dependencies = [ - "anyhow", - "prost", - "reqwest", - "rskafka", - "serde", - "serde_json", - "tantivy", - "tempfile", - "tokio", - "tonic", - "tonic-build", - "tracing", - "tracing-subscriber", -] - [[package]] name = "serde" version = "1.0.229" diff --git a/search/Cargo.toml b/search/Cargo.toml index 328df5b..3d90a88 100644 --- a/search/Cargo.toml +++ b/search/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "sentry-search" +name = "cairnobs-search" version = "0.1.0" edition = "2021" license = "AGPL-3.0-only" -description = "Sentry Tantivy-backed full-text search service" +description = "Cairn OBS Tantivy-backed full-text search service" [[bin]] -name = "sentry-search" +name = "cairnobs-search" path = "src/main.rs" [dependencies] diff --git a/search/Dockerfile b/search/Dockerfile index 0d45b8a..47f8cc3 100644 --- a/search/Dockerfile +++ b/search/Dockerfile @@ -1,6 +1,6 @@ # Build context must be the repo root (sentry/), since this needs both # search/ and proto/: -# docker build -f search/Dockerfile -t sentry-search . +# docker build -f search/Dockerfile -t cairnobs-search . # # Unlike /agent, this doesn't need musl/static linking -- it's a normal # server-side container, not an edge-deployed binary, and both Tantivy and @@ -16,5 +16,5 @@ WORKDIR /src/search RUN cargo build --release FROM gcr.io/distroless/cc-debian12 -COPY --from=builder /src/search/target/release/sentry-search /sentry-search -ENTRYPOINT ["/sentry-search"] +COPY --from=builder /src/search/target/release/cairnobs-search /cairnobs-search +ENTRYPOINT ["/cairnobs-search"] diff --git a/search/README.md b/search/README.md index afafd6a..59d949d 100644 --- a/search/README.md +++ b/search/README.md @@ -44,7 +44,7 @@ a second copy of the row. `src/registry.rs`'s `IndexRegistry` searches: empty resolves to the single default index every deployment already had; a non-empty value opens (on first use) a dedicated index under `TENANTS_INDEX_PATH` -(default `/var/lib/sentry-search/tenants/`, matching +(default `/var/lib/cairnobs-search/tenants/`, matching `deploy/operator`'s and `enterprise/internal/rbacstore`'s existing path convention). `tenant_id` is set only by a trusted server-side caller (`enterprise/internal/searchclient`, from the authenticated request @@ -109,7 +109,7 @@ discovered dynamically. Whatever Tantivy's own `QueryParser` supports against the `message` field: plain terms, `"exact phrase"` queries, and `foo*` wildcards. Not -documented further here because it's Tantivy's syntax, not Sentry's — see +documented further here because it's Tantivy's syntax, not Cairn OBS's — see [Tantivy's query parser docs](https://docs.rs/tantivy/latest/tantivy/query/struct.QueryParser.html) for the full grammar. No unified query language yet; that's Phase 2. @@ -123,9 +123,9 @@ Environment variables (see `src/config.rs`): | `REDPANDA_BROKERS` | `localhost:9092` | Comma-separated broker list | | `REDPANDA_TOPIC` | `sentry.logs.raw` | Must match `/ingest`'s topic | | `REDPANDA_TOPIC_PARTITIONS` | `6` | Must match what `/transport/provision-topics.sh` created | -| `INDEX_PATH` | `/var/lib/sentry-search/index` | Default (non-tenant) Tantivy index directory | -| `TENANTS_INDEX_PATH` | `/var/lib/sentry-search/tenants` | Per-tenant index directories live under here, one subdirectory per tenant_id (Phase 4) | -| `OFFSETS_PATH` | `/var/lib/sentry-search/offsets.json` | Offset tracking file | +| `INDEX_PATH` | `/var/lib/cairnobs-search/index` | Default (non-tenant) Tantivy index directory | +| `TENANTS_INDEX_PATH` | `/var/lib/cairnobs-search/tenants` | Per-tenant index directories live under here, one subdirectory per tenant_id (Phase 4) | +| `OFFSETS_PATH` | `/var/lib/cairnobs-search/offsets.json` | Offset tracking file | | `COMMIT_INTERVAL_MS` | `2000` | How often buffered writes become searchable | | `ENTERPRISE_AUTH_URL` | (empty) | Enables `tenants::ActiveTenantTracker` -- empty means write-routing has no active-tenant gate, same as every deployment before Phase 4. Must be set together with `ENTERPRISE_AUTH_SERVICE_TOKEN` below, or `Config::load` fails | | `ENTERPRISE_AUTH_SERVICE_TOKEN` | (empty) | RoleService Bearer credential for `GET /internal/active-tenants`, minted via `enterprise-auth -mint-service-token search` | @@ -166,5 +166,5 @@ unreachable server), not a fake HTTP client substituted in. ```sh # from the repo root, not search/ -docker build -f search/Dockerfile -t sentry-search . +docker build -f search/Dockerfile -t cairnobs-search . ``` diff --git a/search/deny.toml b/search/deny.toml index e6a45df..9517449 100644 --- a/search/deny.toml +++ b/search/deny.toml @@ -88,7 +88,7 @@ ignore = [ # List of explicitly allowed licenses # See https://spdx.org/licenses/ for list of possible licenses # [possible values: any SPDX 3.11 short identifier (+ optional exception)]. -# Sentry's own AGPLv3-project license policy (Phase 6 license audit -- +# Cairn OBS's own AGPLv3-project license policy (Phase 6 license audit -- # see /docs/compliance/license-policy.md for the full rationale per # category). Every license actually found in this crate's dependency # tree at audit time is listed explicitly here, not just the common diff --git a/search/src/config.rs b/search/src/config.rs index bd59360..f6f9487 100644 --- a/search/src/config.rs +++ b/search/src/config.rs @@ -20,7 +20,7 @@ pub struct Config { /// never turned on ingest's TenantResolver, still lands in. Default /// matches the path convention deploy/operator's Tenant controller /// and enterprise/internal/rbacstore's seeded default data source - /// already assume (`/var/lib/sentry-search/tenants/`). + /// already assume (`/var/lib/cairnobs-search/tenants/`). pub tenants_index_path: PathBuf, /// Base URL of enterprise-auth's HTTP API, e.g. /// `http://enterprise-auth:8082` -- same env var name and "empty @@ -68,15 +68,15 @@ impl Config { .map(str::to_string) .collect(), redpanda_topic: getenv("REDPANDA_TOPIC", "sentry.logs.raw"), - index_path: PathBuf::from(getenv("INDEX_PATH", "/var/lib/sentry-search/index")), + index_path: PathBuf::from(getenv("INDEX_PATH", "/var/lib/cairnobs-search/index")), offsets_path: PathBuf::from(getenv( "OFFSETS_PATH", - "/var/lib/sentry-search/offsets.json", + "/var/lib/cairnobs-search/offsets.json", )), commit_interval: Duration::from_millis(commit_interval_ms), tenants_index_path: PathBuf::from(getenv( "TENANTS_INDEX_PATH", - "/var/lib/sentry-search/tenants", + "/var/lib/cairnobs-search/tenants", )), enterprise_auth_url, enterprise_auth_service_token, diff --git a/search/src/main.rs b/search/src/main.rs index 42b6fbb..24c2c24 100644 --- a/search/src/main.rs +++ b/search/src/main.rs @@ -10,7 +10,7 @@ pub mod logsv1 { tonic::include_proto!("sentry.logs.v1"); } pub mod searchv1 { - tonic::include_proto!("sentry.search.v1"); + tonic::include_proto!("cairnobs.search.v1"); } use anyhow::{Context, Result}; diff --git a/storage/README.md b/storage/README.md index a557166..9bea3bc 100644 --- a/storage/README.md +++ b/storage/README.md @@ -1,6 +1,6 @@ # storage -ClickHouse schema and migration tooling for Sentry's analytical store. +ClickHouse schema and migration tooling for Cairn OBS's analytical store. ## Schema @@ -79,7 +79,7 @@ split multi-statement files — keep each migration to a single statement. ```sh docker compose up -d # starts a standalone ClickHouse for local work -CLICKHOUSE_PASSWORD=sentry-dev-only ./migrate.sh # applies migrations/*.sql +CLICKHOUSE_PASSWORD=cairnobs-dev-only ./migrate.sh # applies migrations/*.sql ``` `CLICKHOUSE_PASSWORD` here has to match whatever `docker-compose.yml` set diff --git a/storage/docker-compose.yml b/storage/docker-compose.yml index bad33d6..b8b16d7 100644 --- a/storage/docker-compose.yml +++ b/storage/docker-compose.yml @@ -5,7 +5,7 @@ services: clickhouse: image: clickhouse/clickhouse-server:24.8 - container_name: sentry-clickhouse + container_name: cairnobs-clickhouse ports: - "8123:8123" # HTTP interface, used by migrate.sh - "9000:9000" # native protocol, used by ingest @@ -17,7 +17,7 @@ services: # explicitly-empty CLICKHOUSE_PASSWORD="" still triggers it. Not a # real secret; see the root docker-compose.yml for the full # explanation. - CLICKHOUSE_PASSWORD: "sentry-dev-only" + CLICKHOUSE_PASSWORD: "cairnobs-dev-only" volumes: - clickhouse-data:/var/lib/clickhouse ulimits: diff --git a/terraform/README.md b/terraform/README.md index e291c80..84d7665 100644 --- a/terraform/README.md +++ b/terraform/README.md @@ -1,11 +1,11 @@ -# terraform-provider-sentry +# terraform-provider-cairnobs -Sentry's Terraform provider -- `CLAUDE.md`'s "Repo conventions" section -names this a first-class deliverable alongside `sentryctl` +Cairn OBS's Terraform provider -- `CLAUDE.md`'s "Repo conventions" section +names this a first-class deliverable alongside `cairnobsctl` ("CLI and Terraform provider are first-class, not afterthoughts"), but no phase before this one had actually built any of it. Four resources -so far (`sentry_dashboard`, `sentry_dashboard_panel`, `sentry_alert_rule`, -`sentry_notification_target`), each paired with a read-only data source +so far (`cairnobs_dashboard`, `cairnobs_dashboard_panel`, `cairnobs_alert_rule`, +`cairnobs_notification_target`), each paired with a read-only data source of the same name, not a finished provider, built on [HashiCorp's `terraform-plugin-framework`][framework] (the actively-developed library, not the legacy SDKv2 -- there's no existing @@ -21,20 +21,20 @@ framework HashiCorp itself steers new providers away from). of a future Terraform provider: one JSON contract, multiple callers (web export, CLI apply, eventually a provider)." This provider is that third caller -- `internal/provider/client.go` talks the exact same JSON shape -`sentryctl dashboards apply` and the web UI's Export JSON button already +`cairnobsctl dashboards apply` and the web UI's Export JSON button already use against `api/dashboards.Handler`, not a new contract invented for -Terraform's sake. `sentry_dashboard_panel` follows the same contract's +Terraform's sake. `cairnobs_dashboard_panel` follows the same contract's panel endpoints, as its own resource rather than a block nested inside -`sentry_dashboard` -- see "Panels are their own resource" below. -`sentry_alert_rule` follows against `alerting`'s own `POST`/`GET`/ +`cairnobs_dashboard` -- see "Panels are their own resource" below. +`cairnobs_alert_rule` follows against `alerting`'s own `POST`/`GET`/ `DELETE /rules[/{id}]` -- the natural next resource, and a real second service (`alerting` is a genuinely separate deployment from `api`, its own base URL), so building it exercised that this provider can talk to -more than one Sentry service, not just repeat the dashboards pattern -against the same endpoint. `sentry_notification_target` rounds these -out -- `sentry_alert_rule.notification_target_id` needs something to +more than one Cairn OBS service, not just repeat the dashboards pattern +against the same endpoint. `cairnobs_notification_target` rounds these +out -- `cairnobs_alert_rule.notification_target_id` needs something to actually point at, and without this resource that id could only ever -come from outside Terraform (`sentryctl`, `curl`, the web UI), +come from outside Terraform (`cairnobsctl`, `curl`, the web UI), undermining the point of managing rules as code at all. ## What's built @@ -42,18 +42,18 @@ undermining the point of managing rules as code at all. ```hcl terraform { required_providers { - sentry = { - source = "registry.terraform.io/sentry/sentry" + cairnobs = { + source = "registry.terraform.io/cairnobs/cairnobs" } } } -provider "sentry" { - endpoint = "http://localhost:8080" # or $SENTRY_API_ENDPOINT - token = var.sentry_api_token # or $SENTRY_API_TOKEN -- optional, only needed once enterprise-auth enforcement is on +provider "cairnobs" { + endpoint = "http://localhost:8080" # or $CAIRNOBS_API_ENDPOINT + token = var.cairnobs_api_token # or $CAIRNOBS_API_TOKEN -- optional, only needed once enterprise-auth enforcement is on } -resource "sentry_dashboard" "example" { +resource "cairnobs_dashboard" "example" { name = "Checkout Errors" description = "5xx rate and latency for the checkout service" # default_earliest/default_latest are optional -- left unset, the API @@ -64,68 +64,68 @@ resource "sentry_dashboard" "example" { } ``` -Supports `terraform import sentry_dashboard.example `. +Supports `terraform import cairnobs_dashboard.example `. **Panels are their own resource, not a nested block.** `api/dashboards. Handler` exposes panel CRUD as its own endpoints (`POST`/`PUT`/ `DELETE /dashboards/{id}/panels[/{panelId}]`) -- a panel belongs to exactly one dashboard, has its own lifecycle, and is created/updated/ deleted independently, never by rewriting a dashboard's whole panel -list, so `sentry_dashboard_panel` follows that shape rather than a +list, so `cairnobs_dashboard_panel` follows that shape rather than a nested list block (which would force every panel to be rewritten on any single panel's change, hiding fine-grained diffs a separate resource shows naturally): ```hcl -resource "sentry_dashboard_panel" "example" { - dashboard_id = sentry_dashboard.example.id +resource "cairnobs_dashboard_panel" "example" { + dashboard_id = cairnobs_dashboard.example.id title = "5xx rate over time" query = "status>=500 | timechart count" viz_type = "line" # table, line, bar, single_stat, or top_n # query_language never accepts "sql" for panels -- the API rejects it # outright (dashboards only support pipe-syntax queries, since the # time-range picker is injected as leading query terms). Unlike - # sentry_alert_rule/sentry_notification_target, this resource + # cairnobs_alert_rule/cairnobs_notification_target, this resource # supports a real in-place update (api/dashboards.Handler has a real # PUT for panels) -- only dashboard_id forces a destroy-and-recreate, # since there's no API operation to move a panel between dashboards. } ``` -Supports `terraform import sentry_dashboard_panel.example +Supports `terraform import cairnobs_dashboard_panel.example /` -- a bare panel ID isn't enough on its own, since `Read` needs the parent `dashboard_id` to know where to look (see `client.go`'s `getPanel` doc comment for why: there's no standalone `GET` for a single panel). ```hcl -provider "sentry" { - endpoint = "http://localhost:8080" # or $SENTRY_API_ENDPOINT - alerting_endpoint = "http://localhost:8081" # or $SENTRY_ALERTING_API_ENDPOINT -- alerting is a separate service, own base URL - token = var.sentry_api_token # or $SENTRY_API_TOKEN -- shared by both services, same as sentryctl's one $SENTRYCTL_TOKEN +provider "cairnobs" { + endpoint = "http://localhost:8080" # or $CAIRNOBS_API_ENDPOINT + alerting_endpoint = "http://localhost:8081" # or $CAIRNOBS_ALERTING_API_ENDPOINT -- alerting is a separate service, own base URL + token = var.cairnobs_api_token # or $CAIRNOBS_API_TOKEN -- shared by both services, same as cairnobsctl's one $CAIRNOBSCTL_TOKEN } -resource "sentry_notification_target" "ops" { +resource "cairnobs_notification_target" "ops" { name = "Ops Webhook" kind = "webhook" - webhook_url = "https://ops.example.com/hooks/sentry-alerts" + webhook_url = "https://ops.example.com/hooks/cairnobs-alerts" } -resource "sentry_alert_rule" "example" { +resource "cairnobs_alert_rule" "example" { name = "Checkout 5xx spike" query = "service=checkout status>=500 | stats count" condition_type = "threshold" comparator = "gt" threshold_value = 50 eval_interval_seconds = 60 - notification_target_id = sentry_notification_target.ops.id + notification_target_id = cairnobs_notification_target.ops.id } ``` -Supports `terraform import sentry_alert_rule.example ` and -`terraform import sentry_notification_target.example `. +Supports `terraform import cairnobs_alert_rule.example ` and +`terraform import cairnobs_notification_target.example `. -**`sentry_alert_rule` and `sentry_notification_target` are both +**`cairnobs_alert_rule` and `cairnobs_notification_target` are both create/destroy only, not update-in-place.** `alerting`'s REST API has no `PUT /rules/{id}` or `PUT /targets/{id}` at all -- confirmed down to `rulestore.Store`/`notifystore.Store`, both of which have @@ -144,7 +144,7 @@ surfacing it in the plan output the way `RequiresReplace` does. Adding real `PUT` endpoints to `alerting` would remove this constraint, but is a change to a different module's REST API, out of scope for this pass. -**`sentry_notification_target`'s `secret` attribute is `Sensitive` but +**`cairnobs_notification_target`'s `secret` attribute is `Sensitive` but still lands in Terraform state in plaintext.** `alerting`'s own `GET /targets/{id}` returns `secret` unredacted (confirmed in `notifystore/store.go` -- no redaction at the store or handler layer, an @@ -159,32 +159,32 @@ than left implicit. ## Data sources Each resource above has a matching read-only data source (`data -"sentry_dashboard"`, `data "sentry_dashboard_panel"`, `data -"sentry_alert_rule"`, `data "sentry_notification_target"`) -- a lookup +"cairnobs_dashboard"`, `data "cairnobs_dashboard_panel"`, `data +"cairnobs_alert_rule"`, `data "cairnobs_notification_target"`) -- a lookup against the same endpoint the matching resource's own `Read` already uses, nothing new added to `client.go` beyond that. Mechanical and low-risk by design: no new architectural question, no new external service, no new write path -- just reusing the resource's own model/ conversion functions (`dashboardModelFromAPI` etc.) against `Required` input instead of a full config. Three of the four take a single -`Required` `id`; `sentry_dashboard_panel`'s takes both `dashboard_id` +`Required` `id`; `cairnobs_dashboard_panel`'s takes both `dashboard_id` and `id` (both `Required`), matching `getPanel`'s own two-argument shape -- there's no standalone lookup for a panel by ID alone. ```hcl -data "sentry_notification_target" "ops" { +data "cairnobs_notification_target" "ops" { id = "target-abc123" } -resource "sentry_alert_rule" "checkout_5xx" { +resource "cairnobs_alert_rule" "checkout_5xx" { # ... - notification_target_id = data.sentry_notification_target.ops.id + notification_target_id = data.cairnobs_notification_target.ops.id } ``` -`sentry_notification_target`'s data source has the same `secret` +`cairnobs_notification_target`'s data source has the same `secret` caveat its resource does -- `Sensitive`, but a real value visible in -Terraform state; see "`sentry_notification_target`'s `secret` +Terraform state; see "`cairnobs_notification_target`'s `secret` attribute" above. **Also not built, all real and disclosed, not attempted here:** @@ -197,7 +197,7 @@ attribute" above. `enterprise/` is AGPLv3 same as this provider module; the blocker is purely that the underlying API isn't idempotent-safe yet.) - Publishing to the real Terraform Registry -- `main.go`'s `Address` - (`registry.terraform.io/sentry/sentry`) is the address a real + (`registry.terraform.io/cairnobs/cairnobs`) is the address a real publication would use, but nothing has actually been published; local use is via `~/.terraformrc`'s `dev_overrides` (see "Building & testing" below) or a local provider mirror. @@ -211,7 +211,7 @@ go test ./... ``` `internal/provider/client_test.go` runs real HTTP round trips against a -`httptest.Server` (same pattern `cli/cmd/sentryctl`'s own tests use +`httptest.Server` (same pattern `cli/cmd/cairnobsctl`'s own tests use against the same `api/dashboards`/`alerting` endpoints) -- real request construction (method, path, `Authorization` header, JSON body), real response parsing, including the 404-vs-other-error distinction @@ -266,11 +266,11 @@ for real." # local dev override, so `terraform` picks up a locally-built binary # instead of trying to download from the registry (which nothing has # been published to -- see "What's built" above) -go build -o terraform-provider-sentry . +go build -o terraform-provider-cairnobs . cat <<'EOF' >> ~/.terraformrc provider_installation { dev_overrides { - "registry.terraform.io/sentry/sentry" = "/absolute/path/to/this/directory" + "registry.terraform.io/cairnobs/cairnobs" = "/absolute/path/to/this/directory" } direct {} } diff --git a/terraform/examples/data-sources/cairnobs_alert_rule/data-source.tf b/terraform/examples/data-sources/cairnobs_alert_rule/data-source.tf new file mode 100644 index 0000000..191a01e --- /dev/null +++ b/terraform/examples/data-sources/cairnobs_alert_rule/data-source.tf @@ -0,0 +1,7 @@ +data "cairnobs_alert_rule" "checkout_5xx" { + id = "an-existing-rule-id" +} + +output "checkout_5xx_notification_target" { + value = data.cairnobs_alert_rule.checkout_5xx.notification_target_id +} diff --git a/terraform/examples/data-sources/cairnobs_dashboard/data-source.tf b/terraform/examples/data-sources/cairnobs_dashboard/data-source.tf new file mode 100644 index 0000000..25956a0 --- /dev/null +++ b/terraform/examples/data-sources/cairnobs_dashboard/data-source.tf @@ -0,0 +1,7 @@ +data "cairnobs_dashboard" "checkout_errors" { + id = "an-existing-dashboard-id" +} + +output "checkout_errors_default_earliest" { + value = data.cairnobs_dashboard.checkout_errors.default_earliest +} diff --git a/terraform/examples/data-sources/sentry_notification_target/data-source.tf b/terraform/examples/data-sources/cairnobs_notification_target/data-source.tf similarity index 62% rename from terraform/examples/data-sources/sentry_notification_target/data-source.tf rename to terraform/examples/data-sources/cairnobs_notification_target/data-source.tf index fe90eaa..0b79604 100644 --- a/terraform/examples/data-sources/sentry_notification_target/data-source.tf +++ b/terraform/examples/data-sources/cairnobs_notification_target/data-source.tf @@ -1,16 +1,16 @@ -# secret comes back unredacted (see the sentry_notification_target +# secret comes back unredacted (see the cairnobs_notification_target # resource's schema doc comment) -- this data source's "secret" # attribute is Sensitive for the same reason. -data "sentry_notification_target" "ops_webhook" { +data "cairnobs_notification_target" "ops_webhook" { id = "an-existing-target-id" } -resource "sentry_alert_rule" "checkout_5xx" { +resource "cairnobs_alert_rule" "checkout_5xx" { name = "Checkout 5xx spike" query = "service=checkout status>=500 | stats count" condition_type = "threshold" comparator = "gt" threshold_value = 50 eval_interval_seconds = 60 - notification_target_id = data.sentry_notification_target.ops_webhook.id + notification_target_id = data.cairnobs_notification_target.ops_webhook.id } diff --git a/terraform/examples/data-sources/sentry_alert_rule/data-source.tf b/terraform/examples/data-sources/sentry_alert_rule/data-source.tf deleted file mode 100644 index c991029..0000000 --- a/terraform/examples/data-sources/sentry_alert_rule/data-source.tf +++ /dev/null @@ -1,7 +0,0 @@ -data "sentry_alert_rule" "checkout_5xx" { - id = "an-existing-rule-id" -} - -output "checkout_5xx_notification_target" { - value = data.sentry_alert_rule.checkout_5xx.notification_target_id -} diff --git a/terraform/examples/data-sources/sentry_dashboard/data-source.tf b/terraform/examples/data-sources/sentry_dashboard/data-source.tf deleted file mode 100644 index e28e4bf..0000000 --- a/terraform/examples/data-sources/sentry_dashboard/data-source.tf +++ /dev/null @@ -1,7 +0,0 @@ -data "sentry_dashboard" "checkout_errors" { - id = "an-existing-dashboard-id" -} - -output "checkout_errors_default_earliest" { - value = data.sentry_dashboard.checkout_errors.default_earliest -} diff --git a/terraform/examples/provider/provider.tf b/terraform/examples/provider/provider.tf index 315365e..6216efb 100644 --- a/terraform/examples/provider/provider.tf +++ b/terraform/examples/provider/provider.tf @@ -1,22 +1,22 @@ terraform { required_providers { - sentry = { - source = "registry.terraform.io/sentry/sentry" + cairnobs = { + source = "registry.terraform.io/cairnobs/cairnobs" } } } -variable "sentry_api_token" { +variable "cairnobs_api_token" { type = string default = null sensitive = true } -provider "sentry" { - # Both optional -- default to $SENTRY_API_ENDPOINT/$SENTRY_API_TOKEN, - # then http://localhost:8080/no token, matching sentryctl's own - # defaults (cli/cmd/sentryctl/main.go). token is only required once a +provider "cairnobs" { + # Both optional -- default to $CAIRNOBS_API_ENDPOINT/$CAIRNOBS_API_TOKEN, + # then http://localhost:8080/no token, matching cairnobsctl's own + # defaults (cli/cmd/cairnobsctl/main.go). token is only required once a # deployment turns on enterprise-auth enforcement. endpoint = "http://localhost:8080" - token = var.sentry_api_token + token = var.cairnobs_api_token } diff --git a/terraform/examples/resources/cairnobs_alert_rule/import.sh b/terraform/examples/resources/cairnobs_alert_rule/import.sh new file mode 100644 index 0000000..31bf86a --- /dev/null +++ b/terraform/examples/resources/cairnobs_alert_rule/import.sh @@ -0,0 +1 @@ +terraform import cairnobs_alert_rule.checkout_5xx diff --git a/terraform/examples/resources/sentry_alert_rule/resource.tf b/terraform/examples/resources/cairnobs_alert_rule/resource.tf similarity index 71% rename from terraform/examples/resources/sentry_alert_rule/resource.tf rename to terraform/examples/resources/cairnobs_alert_rule/resource.tf index 0fd6a38..6e0a872 100644 --- a/terraform/examples/resources/sentry_alert_rule/resource.tf +++ b/terraform/examples/resources/cairnobs_alert_rule/resource.tf @@ -1,10 +1,10 @@ -resource "sentry_notification_target" "ops_webhook" { +resource "cairnobs_notification_target" "ops_webhook" { name = "Ops Webhook" kind = "webhook" - webhook_url = "https://ops.example.com/hooks/sentry-alerts" + webhook_url = "https://ops.example.com/hooks/cairnobs-alerts" } -resource "sentry_alert_rule" "checkout_5xx" { +resource "cairnobs_alert_rule" "checkout_5xx" { name = "Checkout 5xx spike" query = "service=checkout status>=500 | stats count" condition_type = "threshold" @@ -12,7 +12,7 @@ resource "sentry_alert_rule" "checkout_5xx" { threshold_value = 50 eval_interval_seconds = 60 for_minutes = 5 - notification_target_id = sentry_notification_target.ops_webhook.id + notification_target_id = cairnobs_notification_target.ops_webhook.id } # Create/destroy only -- alerting has no PUT /rules/{id} today, so diff --git a/terraform/examples/resources/cairnobs_dashboard/import.sh b/terraform/examples/resources/cairnobs_dashboard/import.sh new file mode 100644 index 0000000..80e730c --- /dev/null +++ b/terraform/examples/resources/cairnobs_dashboard/import.sh @@ -0,0 +1 @@ +terraform import cairnobs_dashboard.checkout_errors diff --git a/terraform/examples/resources/sentry_dashboard/resource.tf b/terraform/examples/resources/cairnobs_dashboard/resource.tf similarity index 88% rename from terraform/examples/resources/sentry_dashboard/resource.tf rename to terraform/examples/resources/cairnobs_dashboard/resource.tf index 21de528..e761711 100644 --- a/terraform/examples/resources/sentry_dashboard/resource.tf +++ b/terraform/examples/resources/cairnobs_dashboard/resource.tf @@ -1,4 +1,4 @@ -resource "sentry_dashboard" "checkout_errors" { +resource "cairnobs_dashboard" "checkout_errors" { name = "Checkout Errors" description = "5xx rate and latency for the checkout service" diff --git a/terraform/examples/resources/sentry_dashboard_panel/import.sh b/terraform/examples/resources/cairnobs_dashboard_panel/import.sh similarity index 70% rename from terraform/examples/resources/sentry_dashboard_panel/import.sh rename to terraform/examples/resources/cairnobs_dashboard_panel/import.sh index 6a775bb..71af83c 100644 --- a/terraform/examples/resources/sentry_dashboard_panel/import.sh +++ b/terraform/examples/resources/cairnobs_dashboard_panel/import.sh @@ -1,4 +1,4 @@ # "dashboard_id/panel_id" -- a bare panel id isn't enough to import # from, since Read needs the parent dashboard_id to know where to look # (there's no standalone GET for a single panel). -terraform import sentry_dashboard_panel.error_rate / +terraform import cairnobs_dashboard_panel.error_rate / diff --git a/terraform/examples/resources/sentry_dashboard_panel/resource.tf b/terraform/examples/resources/cairnobs_dashboard_panel/resource.tf similarity index 66% rename from terraform/examples/resources/sentry_dashboard_panel/resource.tf rename to terraform/examples/resources/cairnobs_dashboard_panel/resource.tf index e292ed4..0e36f57 100644 --- a/terraform/examples/resources/sentry_dashboard_panel/resource.tf +++ b/terraform/examples/resources/cairnobs_dashboard_panel/resource.tf @@ -1,9 +1,9 @@ -resource "sentry_dashboard" "checkout_errors" { +resource "cairnobs_dashboard" "checkout_errors" { name = "Checkout Errors" } -resource "sentry_dashboard_panel" "error_rate" { - dashboard_id = sentry_dashboard.checkout_errors.id +resource "cairnobs_dashboard_panel" "error_rate" { + dashboard_id = cairnobs_dashboard.checkout_errors.id title = "5xx rate over time" query = "service=checkout status>=500 | timechart count" viz_type = "line" @@ -13,7 +13,7 @@ resource "sentry_dashboard_panel" "error_rate" { height = 4 } -# Unlike sentry_alert_rule/sentry_notification_target, this resource +# Unlike cairnobs_alert_rule/cairnobs_notification_target, this resource # supports a real in-place update -- api/dashboards.Handler has a real # PUT /dashboards/{id}/panels/{panelId}. Only dashboard_id forces a # destroy-and-recreate (there's no API operation to move a panel between diff --git a/terraform/examples/resources/cairnobs_notification_target/import.sh b/terraform/examples/resources/cairnobs_notification_target/import.sh new file mode 100644 index 0000000..5bbe2f1 --- /dev/null +++ b/terraform/examples/resources/cairnobs_notification_target/import.sh @@ -0,0 +1 @@ +terraform import cairnobs_notification_target.ops_webhook diff --git a/terraform/examples/resources/sentry_notification_target/resource.tf b/terraform/examples/resources/cairnobs_notification_target/resource.tf similarity index 85% rename from terraform/examples/resources/sentry_notification_target/resource.tf rename to terraform/examples/resources/cairnobs_notification_target/resource.tf index d03eab4..678f20a 100644 --- a/terraform/examples/resources/sentry_notification_target/resource.tf +++ b/terraform/examples/resources/cairnobs_notification_target/resource.tf @@ -1,7 +1,7 @@ -resource "sentry_notification_target" "ops_webhook" { +resource "cairnobs_notification_target" "ops_webhook" { name = "Ops Webhook" kind = "webhook" - webhook_url = "https://ops.example.com/hooks/sentry-alerts" + webhook_url = "https://ops.example.com/hooks/cairnobs-alerts" # Optional. Sensitive -- not printed in plan/apply output, but note # it's still stored in Terraform state in plaintext (alerting's own diff --git a/terraform/examples/resources/sentry_alert_rule/import.sh b/terraform/examples/resources/sentry_alert_rule/import.sh deleted file mode 100644 index 25175c0..0000000 --- a/terraform/examples/resources/sentry_alert_rule/import.sh +++ /dev/null @@ -1 +0,0 @@ -terraform import sentry_alert_rule.checkout_5xx diff --git a/terraform/examples/resources/sentry_dashboard/import.sh b/terraform/examples/resources/sentry_dashboard/import.sh deleted file mode 100644 index b4cd4e4..0000000 --- a/terraform/examples/resources/sentry_dashboard/import.sh +++ /dev/null @@ -1 +0,0 @@ -terraform import sentry_dashboard.checkout_errors diff --git a/terraform/examples/resources/sentry_notification_target/import.sh b/terraform/examples/resources/sentry_notification_target/import.sh deleted file mode 100644 index a17fd2b..0000000 --- a/terraform/examples/resources/sentry_notification_target/import.sh +++ /dev/null @@ -1 +0,0 @@ -terraform import sentry_notification_target.ops_webhook diff --git a/terraform/go.mod b/terraform/go.mod index aab46e1..637e086 100644 --- a/terraform/go.mod +++ b/terraform/go.mod @@ -1,4 +1,4 @@ -module github.com/sentry/sentry/terraform +module github.com/cairnobs/cairnobs/terraform go 1.25.8 diff --git a/terraform/internal/provider/alert_rule_data_source.go b/terraform/internal/provider/alert_rule_data_source.go index faa0769..67ab57d 100644 --- a/terraform/internal/provider/alert_rule_data_source.go +++ b/terraform/internal/provider/alert_rule_data_source.go @@ -31,7 +31,7 @@ func (d *alertRuleDataSource) Metadata(_ context.Context, req datasource.Metadat func (d *alertRuleDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { resp.Schema = schema.Schema{ - Description: "Looks up an existing Sentry alert rule by ID. See the sentry_alert_rule resource for how one is created/managed.", + Description: "Looks up an existing Cairn OBS alert rule by ID. See the cairnobs_alert_rule resource for how one is created/managed.", Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ Required: true, diff --git a/terraform/internal/provider/alert_rule_data_source_test.go b/terraform/internal/provider/alert_rule_data_source_test.go index 22cbf7d..ae1e15b 100644 --- a/terraform/internal/provider/alert_rule_data_source_test.go +++ b/terraform/internal/provider/alert_rule_data_source_test.go @@ -15,12 +15,12 @@ func TestAccAlertRuleDataSource_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: ` -provider "sentry" { +provider "cairnobs" { endpoint = "http://localhost:8080" alerting_endpoint = "http://localhost:8081" } -resource "sentry_alert_rule" "test" { +resource "cairnobs_alert_rule" "test" { name = "Data Source Test Rule" query = "status>=500 | stats count" condition_type = "threshold" @@ -30,14 +30,14 @@ resource "sentry_alert_rule" "test" { notification_target_id = "placeholder-target-id" } -data "sentry_alert_rule" "test" { - id = sentry_alert_rule.test.id +data "cairnobs_alert_rule" "test" { + id = cairnobs_alert_rule.test.id } `, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrPair("data.sentry_alert_rule.test", "name", "sentry_alert_rule.test", "name"), - resource.TestCheckResourceAttrPair("data.sentry_alert_rule.test", "query", "sentry_alert_rule.test", "query"), - resource.TestCheckResourceAttrPair("data.sentry_alert_rule.test", "threshold_value", "sentry_alert_rule.test", "threshold_value"), + resource.TestCheckResourceAttrPair("data.cairnobs_alert_rule.test", "name", "cairnobs_alert_rule.test", "name"), + resource.TestCheckResourceAttrPair("data.cairnobs_alert_rule.test", "query", "cairnobs_alert_rule.test", "query"), + resource.TestCheckResourceAttrPair("data.cairnobs_alert_rule.test", "threshold_value", "cairnobs_alert_rule.test", "threshold_value"), ), }, }, diff --git a/terraform/internal/provider/alert_rule_resource.go b/terraform/internal/provider/alert_rule_resource.go index bfaa40d..16cc2f8 100644 --- a/terraform/internal/provider/alert_rule_resource.go +++ b/terraform/internal/provider/alert_rule_resource.go @@ -28,7 +28,7 @@ func newAlertRuleResource() resource.Resource { return &alertRuleResource{} } -// alertRuleResource implements sentry_alert_rule against +// alertRuleResource implements cairnobs_alert_rule against // alerting/internal/httpapi's POST/GET/DELETE /rules[/{id}] endpoints. // // Deliberately create/destroy only, every attribute RequiresReplace: @@ -70,7 +70,7 @@ func (r *alertRuleResource) Metadata(_ context.Context, req resource.MetadataReq func (r *alertRuleResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { replace := []planmodifier.String{stringplanmodifier.RequiresReplace()} resp.Schema = schema.Schema{ - Description: "A Sentry alert rule. Create/destroy only -- alerting has no update endpoint for " + + Description: "A Cairn OBS alert rule. Create/destroy only -- alerting has no update endpoint for " + "rules today (see this resource's Go doc comment), so every attribute below forces a " + "destroy-and-recreate on change, never an in-place update.", Attributes: map[string]schema.Attribute{ @@ -141,7 +141,7 @@ func (r *alertRuleResource) Schema(_ context.Context, _ resource.SchemaRequest, "notification_target_id": schema.StringAttribute{ Required: true, PlanModifiers: replace, - Description: "ID of a sentry_notification_target-managed (or manually created) notification target. The API rejects an empty string. No sentry_notification_target resource exists yet -- see the provider README -- so this has to be a target created some other way (sentryctl, curl, or the web UI) for now.", + Description: "ID of a cairnobs_notification_target-managed (or manually created) notification target. The API rejects an empty string. No cairnobs_notification_target resource exists yet -- see the provider README -- so this has to be a target created some other way (cairnobsctl, curl, or the web UI) for now.", }, "enabled": schema.BoolAttribute{ Optional: true, diff --git a/terraform/internal/provider/alert_rule_resource_test.go b/terraform/internal/provider/alert_rule_resource_test.go index d902cda..d64f620 100644 --- a/terraform/internal/provider/alert_rule_resource_test.go +++ b/terraform/internal/provider/alert_rule_resource_test.go @@ -9,7 +9,7 @@ import ( // Same skip-gated-not-faked posture as TestAccDashboardResource_basic -- // see that test's doc comment. notification_target_id below is a -// placeholder: no sentry_notification_target resource exists yet (see +// placeholder: no cairnobs_notification_target resource exists yet (see // the provider README), so a real run of this test would need a // pre-existing target id supplied some other way; not a blocker for // what this test actually proves, since it has never run against a @@ -20,12 +20,12 @@ func TestAccAlertRuleResource_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: ` -provider "sentry" { +provider "cairnobs" { endpoint = "http://localhost:8080" alerting_endpoint = "http://localhost:8081" } -resource "sentry_alert_rule" "test" { +resource "cairnobs_alert_rule" "test" { name = "Acceptance Test Rule" query = "status>=500 | stats count" condition_type = "threshold" @@ -36,18 +36,18 @@ resource "sentry_alert_rule" "test" { } `, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("sentry_alert_rule.test", "name", "Acceptance Test Rule"), - resource.TestCheckResourceAttr("sentry_alert_rule.test", "comparator", "gt"), - resource.TestCheckResourceAttr("sentry_alert_rule.test", "threshold_value", "5"), - resource.TestCheckResourceAttrSet("sentry_alert_rule.test", "id"), - resource.TestCheckResourceAttrSet("sentry_alert_rule.test", "tenant_id"), + resource.TestCheckResourceAttr("cairnobs_alert_rule.test", "name", "Acceptance Test Rule"), + resource.TestCheckResourceAttr("cairnobs_alert_rule.test", "comparator", "gt"), + resource.TestCheckResourceAttr("cairnobs_alert_rule.test", "threshold_value", "5"), + resource.TestCheckResourceAttrSet("cairnobs_alert_rule.test", "id"), + resource.TestCheckResourceAttrSet("cairnobs_alert_rule.test", "tenant_id"), // Left unset in config -- must come back as the // server's own default (true), same "API default, // not a duplicated Terraform-side one" reasoning - // sentry_dashboard's default_earliest/default_latest + // cairnobs_dashboard's default_earliest/default_latest // use. - resource.TestCheckResourceAttr("sentry_alert_rule.test", "enabled", "true"), - resource.TestCheckResourceAttr("sentry_alert_rule.test", "for_minutes", "0"), + resource.TestCheckResourceAttr("cairnobs_alert_rule.test", "enabled", "true"), + resource.TestCheckResourceAttr("cairnobs_alert_rule.test", "for_minutes", "0"), ), }, { @@ -57,12 +57,12 @@ resource "sentry_alert_rule" "test" { // and changing one (here, the threshold) must plan a // destroy-then-create, never an in-place update. Config: ` -provider "sentry" { +provider "cairnobs" { endpoint = "http://localhost:8080" alerting_endpoint = "http://localhost:8081" } -resource "sentry_alert_rule" "test" { +resource "cairnobs_alert_rule" "test" { name = "Acceptance Test Rule" query = "status>=500 | stats count" condition_type = "threshold" @@ -74,13 +74,13 @@ resource "sentry_alert_rule" "test" { `, ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ - plancheck.ExpectResourceAction("sentry_alert_rule.test", plancheck.ResourceActionDestroyBeforeCreate), + plancheck.ExpectResourceAction("cairnobs_alert_rule.test", plancheck.ResourceActionDestroyBeforeCreate), }, }, - Check: resource.TestCheckResourceAttr("sentry_alert_rule.test", "threshold_value", "10"), + Check: resource.TestCheckResourceAttr("cairnobs_alert_rule.test", "threshold_value", "10"), }, { - ResourceName: "sentry_alert_rule.test", + ResourceName: "cairnobs_alert_rule.test", ImportState: true, ImportStateVerify: true, }, diff --git a/terraform/internal/provider/client.go b/terraform/internal/provider/client.go index 38f6476..95590d5 100644 --- a/terraform/internal/provider/client.go +++ b/terraform/internal/provider/client.go @@ -14,10 +14,10 @@ import ( // client is a thin HTTP client against api/dashboards.Handler's REST // endpoints -- deliberately hand-rolled, not generated from an OpenAPI // spec (none exists in this repo yet), the same "boring, well- -// understood" posture cli/cmd/sentryctl's own httpclient.go already +// understood" posture cli/cmd/cairnobsctl's own httpclient.go already // takes against the same API. Kept separate from that package (not // reused directly) since this one needs typed request/response -// marshaling for Terraform's plan/state model, where sentryctl only +// marshaling for Terraform's plan/state model, where cairnobsctl only // ever needs to pretty-print whatever JSON comes back. type client struct { baseURL string @@ -40,7 +40,7 @@ type apiError struct { } func (e *apiError) Error() string { - return fmt.Sprintf("sentry api: request failed with status %d: %s", e.StatusCode, e.Message) + return fmt.Sprintf("cairnobs api: request failed with status %d: %s", e.StatusCode, e.Message) } func isNotFound(err error) bool { diff --git a/terraform/internal/provider/client_test.go b/terraform/internal/provider/client_test.go index b6db78e..cb080cd 100644 --- a/terraform/internal/provider/client_test.go +++ b/terraform/internal/provider/client_test.go @@ -11,7 +11,7 @@ import ( // TestCreateDashboardSendsExpectedRequest is the same "real // httptest.Server, real HTTP round trip" pattern -// cli/cmd/sentryctl's own tests use against the same api/dashboards +// cli/cmd/cairnobsctl's own tests use against the same api/dashboards // endpoints -- this client has no fake/mock mode, so its tests exercise // real request construction and real response parsing throughout. func TestCreateDashboardSendsExpectedRequest(t *testing.T) { diff --git a/terraform/internal/provider/dashboard_data_source.go b/terraform/internal/provider/dashboard_data_source.go index 0385a4d..2fc9d89 100644 --- a/terraform/internal/provider/dashboard_data_source.go +++ b/terraform/internal/provider/dashboard_data_source.go @@ -34,7 +34,7 @@ func (d *dashboardDataSource) Metadata(_ context.Context, req datasource.Metadat func (d *dashboardDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { resp.Schema = schema.Schema{ - Description: "Looks up an existing Sentry dashboard by ID. See the sentry_dashboard resource for how one is created/managed.", + Description: "Looks up an existing Cairn OBS dashboard by ID. See the cairnobs_dashboard resource for how one is created/managed.", Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ Required: true, diff --git a/terraform/internal/provider/dashboard_data_source_test.go b/terraform/internal/provider/dashboard_data_source_test.go index 584b7f6..cd4f442 100644 --- a/terraform/internal/provider/dashboard_data_source_test.go +++ b/terraform/internal/provider/dashboard_data_source_test.go @@ -14,23 +14,23 @@ func TestAccDashboardDataSource_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: ` -provider "sentry" { +provider "cairnobs" { endpoint = "http://localhost:8080" } -resource "sentry_dashboard" "test" { +resource "cairnobs_dashboard" "test" { name = "Data Source Test Dashboard" description = "created by TestAccDashboardDataSource_basic" } -data "sentry_dashboard" "test" { - id = sentry_dashboard.test.id +data "cairnobs_dashboard" "test" { + id = cairnobs_dashboard.test.id } `, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrPair("data.sentry_dashboard.test", "name", "sentry_dashboard.test", "name"), - resource.TestCheckResourceAttrPair("data.sentry_dashboard.test", "tenant_id", "sentry_dashboard.test", "tenant_id"), - resource.TestCheckResourceAttrPair("data.sentry_dashboard.test", "default_earliest", "sentry_dashboard.test", "default_earliest"), + resource.TestCheckResourceAttrPair("data.cairnobs_dashboard.test", "name", "cairnobs_dashboard.test", "name"), + resource.TestCheckResourceAttrPair("data.cairnobs_dashboard.test", "tenant_id", "cairnobs_dashboard.test", "tenant_id"), + resource.TestCheckResourceAttrPair("data.cairnobs_dashboard.test", "default_earliest", "cairnobs_dashboard.test", "default_earliest"), ), }, }, diff --git a/terraform/internal/provider/dashboard_panel_data_source.go b/terraform/internal/provider/dashboard_panel_data_source.go index 5208c8c..760c7f1 100644 --- a/terraform/internal/provider/dashboard_panel_data_source.go +++ b/terraform/internal/provider/dashboard_panel_data_source.go @@ -32,11 +32,11 @@ func (d *dashboardPanelDataSource) Metadata(_ context.Context, req datasource.Me func (d *dashboardPanelDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { resp.Schema = schema.Schema{ - Description: "Looks up an existing Sentry dashboard panel by (dashboard_id, id). See the sentry_dashboard_panel resource for how one is created/managed.", + Description: "Looks up an existing Cairn OBS dashboard panel by (dashboard_id, id). See the cairnobs_dashboard_panel resource for how one is created/managed.", Attributes: map[string]schema.Attribute{ "dashboard_id": schema.StringAttribute{ Required: true, - Description: "ID of the parent sentry_dashboard.", + Description: "ID of the parent cairnobs_dashboard.", }, "id": schema.StringAttribute{ Required: true, diff --git a/terraform/internal/provider/dashboard_panel_data_source_test.go b/terraform/internal/provider/dashboard_panel_data_source_test.go index 0aa6c7a..a428d3b 100644 --- a/terraform/internal/provider/dashboard_panel_data_source_test.go +++ b/terraform/internal/provider/dashboard_panel_data_source_test.go @@ -14,30 +14,30 @@ func TestAccDashboardPanelDataSource_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: ` -provider "sentry" { +provider "cairnobs" { endpoint = "http://localhost:8080" } -resource "sentry_dashboard" "test" { +resource "cairnobs_dashboard" "test" { name = "Panel Data Source Test Dashboard" } -resource "sentry_dashboard_panel" "test" { - dashboard_id = sentry_dashboard.test.id +resource "cairnobs_dashboard_panel" "test" { + dashboard_id = cairnobs_dashboard.test.id title = "Errors over time" query = "status>=500 | timechart count" viz_type = "line" } -data "sentry_dashboard_panel" "test" { - dashboard_id = sentry_dashboard.test.id - id = sentry_dashboard_panel.test.id +data "cairnobs_dashboard_panel" "test" { + dashboard_id = cairnobs_dashboard.test.id + id = cairnobs_dashboard_panel.test.id } `, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrPair("data.sentry_dashboard_panel.test", "title", "sentry_dashboard_panel.test", "title"), - resource.TestCheckResourceAttrPair("data.sentry_dashboard_panel.test", "query", "sentry_dashboard_panel.test", "query"), - resource.TestCheckResourceAttrPair("data.sentry_dashboard_panel.test", "viz_type", "sentry_dashboard_panel.test", "viz_type"), + resource.TestCheckResourceAttrPair("data.cairnobs_dashboard_panel.test", "title", "cairnobs_dashboard_panel.test", "title"), + resource.TestCheckResourceAttrPair("data.cairnobs_dashboard_panel.test", "query", "cairnobs_dashboard_panel.test", "query"), + resource.TestCheckResourceAttrPair("data.cairnobs_dashboard_panel.test", "viz_type", "cairnobs_dashboard_panel.test", "viz_type"), ), }, }, diff --git a/terraform/internal/provider/dashboard_panel_resource.go b/terraform/internal/provider/dashboard_panel_resource.go index 1fbce89..ff9abb6 100644 --- a/terraform/internal/provider/dashboard_panel_resource.go +++ b/terraform/internal/provider/dashboard_panel_resource.go @@ -26,10 +26,10 @@ func newDashboardPanelResource() resource.Resource { return &dashboardPanelResource{} } -// dashboardPanelResource implements sentry_dashboard_panel against +// dashboardPanelResource implements cairnobs_dashboard_panel against // api/dashboards.Handler's POST/PUT/DELETE // /dashboards/{id}/panels[/{panelId}] endpoints -- a genuinely separate -// resource from sentry_dashboard (own id, own lifecycle, own endpoints), +// resource from cairnobs_dashboard (own id, own lifecycle, own endpoints), // not a nested block on the dashboard resource. That split matches the // API's own shape (a panel is created/updated/deleted independently of // its parent dashboard, never by rewriting the dashboard's whole panel @@ -38,7 +38,7 @@ func newDashboardPanelResource() resource.Resource { // panel to be rewritten on any single panel's change, hiding // fine-grained diffs a separate resource shows naturally. // -// Unlike sentry_alert_rule/sentry_notification_target, this resource +// Unlike cairnobs_alert_rule/cairnobs_notification_target, this resource // supports a real in-place Update -- api/dashboards.Handler actually has // a PUT /dashboards/{id}/panels/{panelId}. Only dashboard_id forces a // replace: UpdatePanel's SQL matches WHERE id = $panelID AND @@ -76,7 +76,7 @@ func (r *dashboardPanelResource) Metadata(_ context.Context, req resource.Metada func (r *dashboardPanelResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ - Description: "A panel on a Sentry dashboard, managed independently of the sentry_dashboard it belongs to.", + Description: "A panel on a Cairn OBS dashboard, managed independently of the cairnobs_dashboard it belongs to.", Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ Computed: true, @@ -86,7 +86,7 @@ func (r *dashboardPanelResource) Schema(_ context.Context, _ resource.SchemaRequ "dashboard_id": schema.StringAttribute{ Required: true, PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()}, - Description: "ID of the sentry_dashboard this panel belongs to. Forces replacement on change -- there is no API operation to move a panel between dashboards, see this resource's Go doc comment.", + Description: "ID of the cairnobs_dashboard this panel belongs to. Forces replacement on change -- there is no API operation to move a panel between dashboards, see this resource's Go doc comment.", }, "title": schema.StringAttribute{ Optional: true, @@ -101,7 +101,7 @@ func (r *dashboardPanelResource) Schema(_ context.Context, _ resource.SchemaRequ Optional: true, Computed: true, Default: stringdefault.StaticString(""), - Description: `"" (auto-detect) or "spl" -- never "sql", the API rejects that for panels specifically (unlike sentry_alert_rule's query_language, which accepts it).`, + Description: `"" (auto-detect) or "spl" -- never "sql", the API rejects that for panels specifically (unlike cairnobs_alert_rule's query_language, which accepts it).`, }, "viz_type": schema.StringAttribute{ Required: true, diff --git a/terraform/internal/provider/dashboard_panel_resource_test.go b/terraform/internal/provider/dashboard_panel_resource_test.go index 7f21678..b9d8a5a 100644 --- a/terraform/internal/provider/dashboard_panel_resource_test.go +++ b/terraform/internal/provider/dashboard_panel_resource_test.go @@ -16,66 +16,66 @@ func TestAccDashboardPanelResource_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: ` -provider "sentry" { +provider "cairnobs" { endpoint = "http://localhost:8080" } -resource "sentry_dashboard" "test" { +resource "cairnobs_dashboard" "test" { name = "Panel Acceptance Test Dashboard" } -resource "sentry_dashboard_panel" "test" { - dashboard_id = sentry_dashboard.test.id +resource "cairnobs_dashboard_panel" "test" { + dashboard_id = cairnobs_dashboard.test.id title = "Errors over time" query = "status>=500 | timechart count" viz_type = "line" } `, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrPair("sentry_dashboard_panel.test", "dashboard_id", "sentry_dashboard.test", "id"), - resource.TestCheckResourceAttr("sentry_dashboard_panel.test", "title", "Errors over time"), - resource.TestCheckResourceAttr("sentry_dashboard_panel.test", "viz_type", "line"), - resource.TestCheckResourceAttrSet("sentry_dashboard_panel.test", "id"), + resource.TestCheckResourceAttrPair("cairnobs_dashboard_panel.test", "dashboard_id", "cairnobs_dashboard.test", "id"), + resource.TestCheckResourceAttr("cairnobs_dashboard_panel.test", "title", "Errors over time"), + resource.TestCheckResourceAttr("cairnobs_dashboard_panel.test", "viz_type", "line"), + resource.TestCheckResourceAttrSet("cairnobs_dashboard_panel.test", "id"), // Left unset in config -- must come back as the // API's own default ("{}"), same "API default, not // a duplicated Terraform-side one" reasoning - // sentry_dashboard's default_earliest/default_latest + // cairnobs_dashboard's default_earliest/default_latest // use. - resource.TestCheckResourceAttr("sentry_dashboard_panel.test", "viz_config", "{}"), + resource.TestCheckResourceAttr("cairnobs_dashboard_panel.test", "viz_config", "{}"), ), }, { - // Update: unlike sentry_alert_rule/sentry_notification_target, + // Update: unlike cairnobs_alert_rule/cairnobs_notification_target, // this really is an in-place update -- api/dashboards.Handler // has a real PUT for panels. Config: ` -provider "sentry" { +provider "cairnobs" { endpoint = "http://localhost:8080" } -resource "sentry_dashboard" "test" { +resource "cairnobs_dashboard" "test" { name = "Panel Acceptance Test Dashboard" } -resource "sentry_dashboard_panel" "test" { - dashboard_id = sentry_dashboard.test.id +resource "cairnobs_dashboard_panel" "test" { + dashboard_id = cairnobs_dashboard.test.id title = "Errors over time (renamed)" query = "status>=500 | timechart count" viz_type = "line" } `, - Check: resource.TestCheckResourceAttr("sentry_dashboard_panel.test", "title", "Errors over time (renamed)"), + Check: resource.TestCheckResourceAttr("cairnobs_dashboard_panel.test", "title", "Errors over time (renamed)"), }, { // "dashboard_id/panel_id" -- see ImportState's doc // comment on splitImportID for why a bare panel ID // isn't enough. - ResourceName: "sentry_dashboard_panel.test", + ResourceName: "cairnobs_dashboard_panel.test", ImportState: true, ImportStateIdFunc: func(s *tfstate.State) (string, error) { - rs, ok := s.RootModule().Resources["sentry_dashboard_panel.test"] + rs, ok := s.RootModule().Resources["cairnobs_dashboard_panel.test"] if !ok { - return "", fmt.Errorf("sentry_dashboard_panel.test not found in state") + return "", fmt.Errorf("cairnobs_dashboard_panel.test not found in state") } return rs.Primary.Attributes["dashboard_id"] + "/" + rs.Primary.Attributes["id"], nil }, diff --git a/terraform/internal/provider/dashboard_resource.go b/terraform/internal/provider/dashboard_resource.go index 6f75197..8b20af8 100644 --- a/terraform/internal/provider/dashboard_resource.go +++ b/terraform/internal/provider/dashboard_resource.go @@ -23,9 +23,9 @@ func newDashboardResource() resource.Resource { return &dashboardResource{} } -// dashboardResource implements sentry_dashboard against +// dashboardResource implements cairnobs_dashboard against // api/dashboards.Handler's POST/GET/PUT/DELETE /dashboards[/{id}] -// endpoints -- the exact same JSON contract cli/cmd/sentryctl's +// endpoints -- the exact same JSON contract cli/cmd/cairnobsctl's // "dashboards apply" and web's Export JSON button already use (see // cli/README.md's "one JSON contract, multiple callers" framing; this // is that third caller). Panels are a separate CRUD surface @@ -54,7 +54,7 @@ func (r *dashboardResource) Metadata(_ context.Context, req resource.MetadataReq func (r *dashboardResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ - Description: "A Sentry dashboard. Panels aren't managed by this resource yet -- see the provider README.", + Description: "A Cairn OBS dashboard. Panels aren't managed by this resource yet -- see the provider README.", Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ Computed: true, @@ -167,7 +167,7 @@ func (r *dashboardResource) Read(ctx context.Context, req resource.ReadRequest, out, err := r.client.getDashboard(ctx, state.ID.ValueString()) if err != nil { if isNotFound(err) { - // Deleted out-of-band (e.g. via web or sentryctl) -- + // Deleted out-of-band (e.g. via web or cairnobsctl) -- // dropping it from state lets the next plan offer to // recreate it, the standard Terraform convention, rather // than failing every subsequent plan/apply until someone diff --git a/terraform/internal/provider/dashboard_resource_test.go b/terraform/internal/provider/dashboard_resource_test.go index e1398ca..76c2e09 100644 --- a/terraform/internal/provider/dashboard_resource_test.go +++ b/terraform/internal/provider/dashboard_resource_test.go @@ -13,7 +13,7 @@ import ( // runner -- HashiCorp's standard pattern, one factory reused by every // acceptance test in this package. var testAccProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ - "sentry": providerserver.NewProtocol6WithError(New("test")()), + "cairnobs": providerserver.NewProtocol6WithError(New("test")()), } // The acceptance test below is gated the same way every other live- @@ -32,49 +32,49 @@ func TestAccDashboardResource_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: ` -provider "sentry" { +provider "cairnobs" { endpoint = "http://localhost:8080" } -resource "sentry_dashboard" "test" { +resource "cairnobs_dashboard" "test" { name = "Acceptance Test Dashboard" description = "created by TestAccDashboardResource_basic" } `, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("sentry_dashboard.test", "name", "Acceptance Test Dashboard"), - resource.TestCheckResourceAttr("sentry_dashboard.test", "description", "created by TestAccDashboardResource_basic"), - resource.TestCheckResourceAttrSet("sentry_dashboard.test", "id"), - resource.TestCheckResourceAttrSet("sentry_dashboard.test", "tenant_id"), + resource.TestCheckResourceAttr("cairnobs_dashboard.test", "name", "Acceptance Test Dashboard"), + resource.TestCheckResourceAttr("cairnobs_dashboard.test", "description", "created by TestAccDashboardResource_basic"), + resource.TestCheckResourceAttrSet("cairnobs_dashboard.test", "id"), + resource.TestCheckResourceAttrSet("cairnobs_dashboard.test", "tenant_id"), // Left unset in config -- must come back as the // server's own defaults (store.go: "-1h"/"now"), not // an empty string, proving the Optional+Computed // schema round-trips the server's default rather // than fighting it with a Terraform-side one. - resource.TestCheckResourceAttr("sentry_dashboard.test", "default_earliest", "-1h"), - resource.TestCheckResourceAttr("sentry_dashboard.test", "default_latest", "now"), + resource.TestCheckResourceAttr("cairnobs_dashboard.test", "default_earliest", "-1h"), + resource.TestCheckResourceAttr("cairnobs_dashboard.test", "default_latest", "now"), ), }, { // Update: name change should apply in place, not // replace (no RequiresReplace plan modifier on name). Config: ` -provider "sentry" { +provider "cairnobs" { endpoint = "http://localhost:8080" } -resource "sentry_dashboard" "test" { +resource "cairnobs_dashboard" "test" { name = "Renamed Dashboard" description = "created by TestAccDashboardResource_basic" } `, - Check: resource.TestCheckResourceAttr("sentry_dashboard.test", "name", "Renamed Dashboard"), + Check: resource.TestCheckResourceAttr("cairnobs_dashboard.test", "name", "Renamed Dashboard"), }, { // Import: re-reads by ID alone and must match what's in // state, proving Read()'s server round trip agrees with // what Create()/Update() last wrote. - ResourceName: "sentry_dashboard.test", + ResourceName: "cairnobs_dashboard.test", ImportState: true, ImportStateVerify: true, }, diff --git a/terraform/internal/provider/notification_target_data_source.go b/terraform/internal/provider/notification_target_data_source.go index 86c86fc..4cf0def 100644 --- a/terraform/internal/provider/notification_target_data_source.go +++ b/terraform/internal/provider/notification_target_data_source.go @@ -31,7 +31,7 @@ func (d *notificationTargetDataSource) Metadata(_ context.Context, req datasourc func (d *notificationTargetDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { resp.Schema = schema.Schema{ - Description: "Looks up an existing Sentry notification target by ID. See the sentry_notification_target resource for how one is created/managed.", + Description: "Looks up an existing Cairn OBS notification target by ID. See the cairnobs_notification_target resource for how one is created/managed.", Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ Required: true, @@ -49,7 +49,7 @@ func (d *notificationTargetDataSource) Schema(_ context.Context, _ datasource.Sc Computed: true, Sensitive: true, Description: "alerting's own GET /targets/{id} returns this unredacted (see the " + - "sentry_notification_target resource's schema doc comment) -- Sensitive here for the " + + "cairnobs_notification_target resource's schema doc comment) -- Sensitive here for the " + "same reason, and the same state-file caveat applies.", }, "created_by": schema.StringAttribute{Computed: true}, diff --git a/terraform/internal/provider/notification_target_data_source_test.go b/terraform/internal/provider/notification_target_data_source_test.go index e1b8985..02e89f1 100644 --- a/terraform/internal/provider/notification_target_data_source_test.go +++ b/terraform/internal/provider/notification_target_data_source_test.go @@ -15,29 +15,29 @@ func TestAccNotificationTargetDataSource_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: ` -provider "sentry" { +provider "cairnobs" { endpoint = "http://localhost:8080" alerting_endpoint = "http://localhost:8081" } -resource "sentry_notification_target" "test" { +resource "cairnobs_notification_target" "test" { name = "Data Source Test Target" kind = "webhook" webhook_url = "https://example.com/hook" secret = "test-secret" } -data "sentry_notification_target" "test" { - id = sentry_notification_target.test.id +data "cairnobs_notification_target" "test" { + id = cairnobs_notification_target.test.id } `, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrPair("data.sentry_notification_target.test", "name", "sentry_notification_target.test", "name"), - resource.TestCheckResourceAttrPair("data.sentry_notification_target.test", "webhook_url", "sentry_notification_target.test", "webhook_url"), + resource.TestCheckResourceAttrPair("data.cairnobs_notification_target.test", "name", "cairnobs_notification_target.test", "name"), + resource.TestCheckResourceAttrPair("data.cairnobs_notification_target.test", "webhook_url", "cairnobs_notification_target.test", "webhook_url"), // Real, not papered over -- secret round-trips // unredacted (see the resource test's equivalent // comment). - resource.TestCheckResourceAttrPair("data.sentry_notification_target.test", "secret", "sentry_notification_target.test", "secret"), + resource.TestCheckResourceAttrPair("data.cairnobs_notification_target.test", "secret", "cairnobs_notification_target.test", "secret"), ), }, }, diff --git a/terraform/internal/provider/notification_target_resource.go b/terraform/internal/provider/notification_target_resource.go index 2df0ba6..23b6b63 100644 --- a/terraform/internal/provider/notification_target_resource.go +++ b/terraform/internal/provider/notification_target_resource.go @@ -23,7 +23,7 @@ func newNotificationTargetResource() resource.Resource { return ¬ificationTargetResource{} } -// notificationTargetResource implements sentry_notification_target +// notificationTargetResource implements cairnobs_notification_target // against alerting/internal/httpapi's POST/GET/DELETE // /targets[/{id}] endpoints. // @@ -54,14 +54,14 @@ func (r *notificationTargetResource) Metadata(_ context.Context, req resource.Me func (r *notificationTargetResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { replace := []planmodifier.String{stringplanmodifier.RequiresReplace()} resp.Schema = schema.Schema{ - Description: "A Sentry alert notification target. Create/destroy only -- alerting has no update " + + Description: "A Cairn OBS alert notification target. Create/destroy only -- alerting has no update " + "endpoint for targets today (see this resource's Go doc comment), so every attribute below " + "forces a destroy-and-recreate on change, never an in-place update.", Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ Computed: true, PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, - Description: "Server-generated target ID -- reference this from a sentry_alert_rule's notification_target_id.", + Description: "Server-generated target ID -- reference this from a cairnobs_alert_rule's notification_target_id.", }, "tenant_id": schema.StringAttribute{ Computed: true, diff --git a/terraform/internal/provider/notification_target_resource_test.go b/terraform/internal/provider/notification_target_resource_test.go index 396fb55..07ed916 100644 --- a/terraform/internal/provider/notification_target_resource_test.go +++ b/terraform/internal/provider/notification_target_resource_test.go @@ -15,12 +15,12 @@ func TestAccNotificationTargetResource_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: ` -provider "sentry" { +provider "cairnobs" { endpoint = "http://localhost:8080" alerting_endpoint = "http://localhost:8081" } -resource "sentry_notification_target" "test" { +resource "cairnobs_notification_target" "test" { name = "Acceptance Test Target" kind = "webhook" webhook_url = "https://example.com/hook" @@ -28,11 +28,11 @@ resource "sentry_notification_target" "test" { } `, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr("sentry_notification_target.test", "name", "Acceptance Test Target"), - resource.TestCheckResourceAttr("sentry_notification_target.test", "kind", "webhook"), - resource.TestCheckResourceAttr("sentry_notification_target.test", "secret", "test-secret"), - resource.TestCheckResourceAttrSet("sentry_notification_target.test", "id"), - resource.TestCheckResourceAttrSet("sentry_notification_target.test", "tenant_id"), + resource.TestCheckResourceAttr("cairnobs_notification_target.test", "name", "Acceptance Test Target"), + resource.TestCheckResourceAttr("cairnobs_notification_target.test", "kind", "webhook"), + resource.TestCheckResourceAttr("cairnobs_notification_target.test", "secret", "test-secret"), + resource.TestCheckResourceAttrSet("cairnobs_notification_target.test", "id"), + resource.TestCheckResourceAttrSet("cairnobs_notification_target.test", "tenant_id"), ), }, { @@ -40,12 +40,12 @@ resource "sentry_notification_target" "test" { // TestAccAlertRuleResource_basic's second step -- // notifystore.Store has no Update either. Config: ` -provider "sentry" { +provider "cairnobs" { endpoint = "http://localhost:8080" alerting_endpoint = "http://localhost:8081" } -resource "sentry_notification_target" "test" { +resource "cairnobs_notification_target" "test" { name = "Renamed Target" kind = "webhook" webhook_url = "https://example.com/hook" @@ -54,10 +54,10 @@ resource "sentry_notification_target" "test" { `, ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ - plancheck.ExpectResourceAction("sentry_notification_target.test", plancheck.ResourceActionDestroyBeforeCreate), + plancheck.ExpectResourceAction("cairnobs_notification_target.test", plancheck.ResourceActionDestroyBeforeCreate), }, }, - Check: resource.TestCheckResourceAttr("sentry_notification_target.test", "name", "Renamed Target"), + Check: resource.TestCheckResourceAttr("cairnobs_notification_target.test", "name", "Renamed Target"), }, { // No ImportStateVerifyIgnore for "secret" -- GET @@ -65,7 +65,7 @@ resource "sentry_notification_target" "test" { // doc comment and TestGetNotificationTargetReturnsSecretUnredacted), // so import-time equality is a real, meaningful // assertion here, not one this test has to paper over. - ResourceName: "sentry_notification_target.test", + ResourceName: "cairnobs_notification_target.test", ImportState: true, ImportStateVerify: true, }, diff --git a/terraform/internal/provider/provider.go b/terraform/internal/provider/provider.go index 4b9bc7e..b96eb28 100644 --- a/terraform/internal/provider/provider.go +++ b/terraform/internal/provider/provider.go @@ -1,4 +1,4 @@ -// Package provider is Sentry's Terraform provider implementation, +// Package provider is Cairn OBS's Terraform provider implementation, // built on HashiCorp's terraform-plugin-framework (not the legacy // SDKv2 -- the framework is the actively-developed, currently- // recommended library for a provider started from scratch, matching @@ -17,22 +17,22 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" ) -var _ provider.Provider = &sentryProvider{} +var _ provider.Provider = &cairnobsProvider{} // New matches providerserver.Serve's expected constructor shape -- // version is threaded through from main.go's -ldflags-injected build // version. func New(version string) func() provider.Provider { return func() provider.Provider { - return &sentryProvider{version: version} + return &cairnobsProvider{version: version} } } -type sentryProvider struct { +type cairnobsProvider struct { version string } -type sentryProviderModel struct { +type cairnobsProviderModel struct { Endpoint types.String `tfsdk:"endpoint"` AlertingEndpoint types.String `tfsdk:"alerting_endpoint"` Token types.String `tfsdk:"token"` @@ -42,7 +42,7 @@ type sentryProviderModel struct { // req.ProviderData -- two separate clients, not one, because `alerting` // is a genuinely separate service with its own base URL (its own // REST API, its own port, sometimes its own deployment) -- same split -// web/src/lib/api.ts's apiBase/alertingBase and cli/cmd/sentryctl's +// web/src/lib/api.ts's apiBase/alertingBase and cli/cmd/cairnobsctl's // --api/--alerting-api already draw, not something invented for this // provider. type providerData struct { @@ -50,36 +50,36 @@ type providerData struct { alerting *client } -func (p *sentryProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) { - resp.TypeName = "sentry" +func (p *cairnobsProvider) Metadata(_ context.Context, _ provider.MetadataRequest, resp *provider.MetadataResponse) { + resp.TypeName = "cairnobs" resp.Version = p.version } -func (p *sentryProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) { +func (p *cairnobsProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *provider.SchemaResponse) { resp.Schema = schema.Schema{ - Description: "Manages Sentry log-aggregation-platform resources. Dashboards, alert rules, and notification targets for now -- tenant/RBAC resources are real, disclosed future work, not built in this pass; see the provider README.", + Description: "Manages Cairn OBS log-aggregation-platform resources. Dashboards, alert rules, and notification targets for now -- tenant/RBAC resources are real, disclosed future work, not built in this pass; see the provider README.", Attributes: map[string]schema.Attribute{ "endpoint": schema.StringAttribute{ Optional: true, Description: "Base URL of the api service, e.g. \"http://localhost:8080\". Defaults to " + - "$SENTRY_API_ENDPOINT, or \"http://localhost:8080\" if that's unset too -- same " + - "default sentryctl's --api/$SENTRYCTL_API_URL uses (cli/cmd/sentryctl/main.go).", + "$CAIRNOBS_API_ENDPOINT, or \"http://localhost:8080\" if that's unset too -- same " + + "default cairnobsctl's --api/$CAIRNOBSCTL_API_URL uses (cli/cmd/cairnobsctl/main.go).", }, "alerting_endpoint": schema.StringAttribute{ Optional: true, Description: "Base URL of the alerting service, e.g. \"http://localhost:8081\" -- a " + "separate service from api, not a path under endpoint above (see " + "/docs/phase-3-alerting-design.md's component boundary). Defaults to " + - "$SENTRY_ALERTING_API_ENDPOINT, or \"http://localhost:8081\" if that's unset too -- " + - "same default sentryctl's --alerting-api/$SENTRYCTL_ALERTING_API_URL uses.", + "$CAIRNOBS_ALERTING_API_ENDPOINT, or \"http://localhost:8081\" if that's unset too -- " + + "same default cairnobsctl's --alerting-api/$CAIRNOBSCTL_ALERTING_API_URL uses.", }, "token": schema.StringAttribute{ Optional: true, Sensitive: true, Description: "Bearer credential sent as \"Authorization: Bearer \" on every request " + "-- required once a deployment configures enterprise-auth (see " + - "/docs/phase-4-rbac-design.md), same as sentryctl's $SENTRYCTL_TOKEN. Defaults to " + - "$SENTRY_API_TOKEN if unset. Set via a variable or environment, never a literal in a " + + "/docs/phase-4-rbac-design.md), same as cairnobsctl's $CAIRNOBSCTL_TOKEN. Defaults to " + + "$CAIRNOBS_API_TOKEN if unset. Set via a variable or environment, never a literal in a " + ".tf file committed to version control.", }, }, @@ -87,11 +87,11 @@ func (p *sentryProvider) Schema(_ context.Context, _ provider.SchemaRequest, res } // Configure resolves endpoint/token the same precedence order -// sentryctl's resolveAPIURL/resolveToken use (explicit config value, +// cairnobsctl's resolveAPIURL/resolveToken use (explicit config value, // then an environment variable, then a hardcoded default) so behavior -// stays predictable across both of this project's Sentry API clients. -func (p *sentryProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { - var config sentryProviderModel +// stays predictable across both of this project's Cairn OBS API clients. +func (p *cairnobsProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) { + var config cairnobsProviderModel resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) if resp.Diagnostics.HasError() { return @@ -99,7 +99,7 @@ func (p *sentryProvider) Configure(ctx context.Context, req provider.ConfigureRe endpoint := config.Endpoint.ValueString() if endpoint == "" { - endpoint = os.Getenv("SENTRY_API_ENDPOINT") + endpoint = os.Getenv("CAIRNOBS_API_ENDPOINT") } if endpoint == "" { endpoint = "http://localhost:8080" @@ -107,7 +107,7 @@ func (p *sentryProvider) Configure(ctx context.Context, req provider.ConfigureRe alertingEndpoint := config.AlertingEndpoint.ValueString() if alertingEndpoint == "" { - alertingEndpoint = os.Getenv("SENTRY_ALERTING_API_ENDPOINT") + alertingEndpoint = os.Getenv("CAIRNOBS_ALERTING_API_ENDPOINT") } if alertingEndpoint == "" { alertingEndpoint = "http://localhost:8081" @@ -115,7 +115,7 @@ func (p *sentryProvider) Configure(ctx context.Context, req provider.ConfigureRe token := config.Token.ValueString() if token == "" { - token = os.Getenv("SENTRY_API_TOKEN") + token = os.Getenv("CAIRNOBS_API_TOKEN") } data := &providerData{ @@ -126,7 +126,7 @@ func (p *sentryProvider) Configure(ctx context.Context, req provider.ConfigureRe resp.ResourceData = data } -func (p *sentryProvider) Resources(_ context.Context) []func() resource.Resource { +func (p *cairnobsProvider) Resources(_ context.Context) []func() resource.Resource { return []func() resource.Resource{ newDashboardResource, newDashboardPanelResource, @@ -135,7 +135,7 @@ func (p *sentryProvider) Resources(_ context.Context) []func() resource.Resource } } -func (p *sentryProvider) DataSources(_ context.Context) []func() datasource.DataSource { +func (p *cairnobsProvider) DataSources(_ context.Context) []func() datasource.DataSource { return []func() datasource.DataSource{ newDashboardDataSource, newDashboardPanelDataSource, diff --git a/terraform/internal/provider/provider_test.go b/terraform/internal/provider/provider_test.go index c48d07c..c145438 100644 --- a/terraform/internal/provider/provider_test.go +++ b/terraform/internal/provider/provider_test.go @@ -40,14 +40,14 @@ func TestDashboardResourceSchemaValid(t *testing.T) { newDashboardResource().Schema(ctx, req, resp) if resp.Diagnostics.HasError() { - t.Fatalf("sentry_dashboard schema has errors: %v", resp.Diagnostics) + t.Fatalf("cairnobs_dashboard schema has errors: %v", resp.Diagnostics) } for _, attr := range []string{ "id", "tenant_id", "name", "description", "default_earliest", "default_latest", "created_by", "created_at", "updated_at", } { if _, ok := resp.Schema.Attributes[attr]; !ok { - t.Errorf("sentry_dashboard schema missing expected attribute %q", attr) + t.Errorf("cairnobs_dashboard schema missing expected attribute %q", attr) } } if !resp.Schema.Attributes["name"].IsRequired() { @@ -60,9 +60,9 @@ func TestDashboardResourceSchemaValid(t *testing.T) { func TestDashboardResourceMetadataSetsTypeName(t *testing.T) { resp := &resource.MetadataResponse{} - newDashboardResource().Metadata(context.Background(), resource.MetadataRequest{ProviderTypeName: "sentry"}, resp) - if resp.TypeName != "sentry_dashboard" { - t.Fatalf("TypeName = %q, want sentry_dashboard", resp.TypeName) + newDashboardResource().Metadata(context.Background(), resource.MetadataRequest{ProviderTypeName: "cairnobs"}, resp) + if resp.TypeName != "cairnobs_dashboard" { + t.Fatalf("TypeName = %q, want cairnobs_dashboard", resp.TypeName) } } @@ -74,7 +74,7 @@ func TestDashboardPanelResourceSchemaValid(t *testing.T) { newDashboardPanelResource().Schema(ctx, req, resp) if resp.Diagnostics.HasError() { - t.Fatalf("sentry_dashboard_panel schema has errors: %v", resp.Diagnostics) + t.Fatalf("cairnobs_dashboard_panel schema has errors: %v", resp.Diagnostics) } for _, attr := range []string{ "id", "dashboard_id", "title", "query", "query_language", "viz_type", "viz_config", @@ -82,7 +82,7 @@ func TestDashboardPanelResourceSchemaValid(t *testing.T) { "earliest_override", "latest_override", "sort_order", "created_at", "updated_at", } { if _, ok := resp.Schema.Attributes[attr]; !ok { - t.Errorf("sentry_dashboard_panel schema missing expected attribute %q", attr) + t.Errorf("cairnobs_dashboard_panel schema missing expected attribute %q", attr) } } if !resp.Schema.Attributes["query"].IsRequired() { @@ -98,9 +98,9 @@ func TestDashboardPanelResourceSchemaValid(t *testing.T) { func TestDashboardPanelResourceMetadataSetsTypeName(t *testing.T) { resp := &resource.MetadataResponse{} - newDashboardPanelResource().Metadata(context.Background(), resource.MetadataRequest{ProviderTypeName: "sentry"}, resp) - if resp.TypeName != "sentry_dashboard_panel" { - t.Fatalf("TypeName = %q, want sentry_dashboard_panel", resp.TypeName) + newDashboardPanelResource().Metadata(context.Background(), resource.MetadataRequest{ProviderTypeName: "cairnobs"}, resp) + if resp.TypeName != "cairnobs_dashboard_panel" { + t.Fatalf("TypeName = %q, want cairnobs_dashboard_panel", resp.TypeName) } } @@ -112,7 +112,7 @@ func TestAlertRuleResourceSchemaValid(t *testing.T) { newAlertRuleResource().Schema(ctx, req, resp) if resp.Diagnostics.HasError() { - t.Fatalf("sentry_alert_rule schema has errors: %v", resp.Diagnostics) + t.Fatalf("cairnobs_alert_rule schema has errors: %v", resp.Diagnostics) } for _, attr := range []string{ "id", "tenant_id", "name", "description", "query", "query_language", @@ -120,7 +120,7 @@ func TestAlertRuleResourceSchemaValid(t *testing.T) { "for_minutes", "renotify_interval_minutes", "notification_target_id", "enabled", "created_by", } { if _, ok := resp.Schema.Attributes[attr]; !ok { - t.Errorf("sentry_alert_rule schema missing expected attribute %q", attr) + t.Errorf("cairnobs_alert_rule schema missing expected attribute %q", attr) } } if !resp.Schema.Attributes["name"].IsRequired() { @@ -133,9 +133,9 @@ func TestAlertRuleResourceSchemaValid(t *testing.T) { func TestAlertRuleResourceMetadataSetsTypeName(t *testing.T) { resp := &resource.MetadataResponse{} - newAlertRuleResource().Metadata(context.Background(), resource.MetadataRequest{ProviderTypeName: "sentry"}, resp) - if resp.TypeName != "sentry_alert_rule" { - t.Fatalf("TypeName = %q, want sentry_alert_rule", resp.TypeName) + newAlertRuleResource().Metadata(context.Background(), resource.MetadataRequest{ProviderTypeName: "cairnobs"}, resp) + if resp.TypeName != "cairnobs_alert_rule" { + t.Fatalf("TypeName = %q, want cairnobs_alert_rule", resp.TypeName) } } @@ -147,14 +147,14 @@ func TestNotificationTargetResourceSchemaValid(t *testing.T) { newNotificationTargetResource().Schema(ctx, req, resp) if resp.Diagnostics.HasError() { - t.Fatalf("sentry_notification_target schema has errors: %v", resp.Diagnostics) + t.Fatalf("cairnobs_notification_target schema has errors: %v", resp.Diagnostics) } for _, attr := range []string{ "id", "tenant_id", "name", "kind", "webhook_url", "payload_template", "headers", "secret", "created_by", } { if _, ok := resp.Schema.Attributes[attr]; !ok { - t.Errorf("sentry_notification_target schema missing expected attribute %q", attr) + t.Errorf("cairnobs_notification_target schema missing expected attribute %q", attr) } } if !resp.Schema.Attributes["name"].IsRequired() { @@ -167,9 +167,9 @@ func TestNotificationTargetResourceSchemaValid(t *testing.T) { func TestNotificationTargetResourceMetadataSetsTypeName(t *testing.T) { resp := &resource.MetadataResponse{} - newNotificationTargetResource().Metadata(context.Background(), resource.MetadataRequest{ProviderTypeName: "sentry"}, resp) - if resp.TypeName != "sentry_notification_target" { - t.Fatalf("TypeName = %q, want sentry_notification_target", resp.TypeName) + newNotificationTargetResource().Metadata(context.Background(), resource.MetadataRequest{ProviderTypeName: "cairnobs"}, resp) + if resp.TypeName != "cairnobs_notification_target" { + t.Fatalf("TypeName = %q, want cairnobs_notification_target", resp.TypeName) } } @@ -181,7 +181,7 @@ func TestDashboardDataSourceSchemaValid(t *testing.T) { newDashboardDataSource().Schema(ctx, req, resp) if resp.Diagnostics.HasError() { - t.Fatalf("sentry_dashboard data source schema has errors: %v", resp.Diagnostics) + t.Fatalf("cairnobs_dashboard data source schema has errors: %v", resp.Diagnostics) } if !resp.Schema.Attributes["id"].IsRequired() { t.Error(`"id" must be Required -- a data source needs it to know what to look up`) @@ -199,7 +199,7 @@ func TestDashboardPanelDataSourceSchemaValid(t *testing.T) { newDashboardPanelDataSource().Schema(ctx, req, resp) if resp.Diagnostics.HasError() { - t.Fatalf("sentry_dashboard_panel data source schema has errors: %v", resp.Diagnostics) + t.Fatalf("cairnobs_dashboard_panel data source schema has errors: %v", resp.Diagnostics) } if !resp.Schema.Attributes["dashboard_id"].IsRequired() { t.Error(`"dashboard_id" must be Required`) @@ -220,7 +220,7 @@ func TestAlertRuleDataSourceSchemaValid(t *testing.T) { newAlertRuleDataSource().Schema(ctx, req, resp) if resp.Diagnostics.HasError() { - t.Fatalf("sentry_alert_rule data source schema has errors: %v", resp.Diagnostics) + t.Fatalf("cairnobs_alert_rule data source schema has errors: %v", resp.Diagnostics) } if !resp.Schema.Attributes["id"].IsRequired() { t.Error(`"id" must be Required`) @@ -238,7 +238,7 @@ func TestNotificationTargetDataSourceSchemaValid(t *testing.T) { newNotificationTargetDataSource().Schema(ctx, req, resp) if resp.Diagnostics.HasError() { - t.Fatalf("sentry_notification_target data source schema has errors: %v", resp.Diagnostics) + t.Fatalf("cairnobs_notification_target data source schema has errors: %v", resp.Diagnostics) } if !resp.Schema.Attributes["id"].IsRequired() { t.Error(`"id" must be Required`) @@ -253,14 +253,14 @@ func TestDataSourcesMetadataSetTypeNames(t *testing.T) { newDS func() datasource.DataSource wantType string }{ - {newDashboardDataSource, "sentry_dashboard"}, - {newDashboardPanelDataSource, "sentry_dashboard_panel"}, - {newAlertRuleDataSource, "sentry_alert_rule"}, - {newNotificationTargetDataSource, "sentry_notification_target"}, + {newDashboardDataSource, "cairnobs_dashboard"}, + {newDashboardPanelDataSource, "cairnobs_dashboard_panel"}, + {newAlertRuleDataSource, "cairnobs_alert_rule"}, + {newNotificationTargetDataSource, "cairnobs_notification_target"}, } for _, c := range cases { resp := &datasource.MetadataResponse{} - c.newDS().Metadata(context.Background(), datasource.MetadataRequest{ProviderTypeName: "sentry"}, resp) + c.newDS().Metadata(context.Background(), datasource.MetadataRequest{ProviderTypeName: "cairnobs"}, resp) if resp.TypeName != c.wantType { t.Errorf("TypeName = %q, want %q", resp.TypeName, c.wantType) } diff --git a/terraform/main.go b/terraform/main.go index 4483e30..60552a8 100644 --- a/terraform/main.go +++ b/terraform/main.go @@ -1,11 +1,11 @@ -// Command terraform-provider-sentry is Sentry's Terraform provider -- -// CLAUDE.md names it a first-class deliverable alongside sentryctl +// Command terraform-provider-cairnobs is Cairn OBS's Terraform provider -- +// CLAUDE.md names it a first-class deliverable alongside cairnobsctl // ("CLI and Terraform provider are first-class, not afterthoughts"), // but this is the first phase to actually build any of it. // // Scoped deliberately narrow to start: one resource -// (internal/provider.dashboardResource, sentry_dashboard), reusing the -// exact same JSON contract cli/cmd/sentryctl's "apply" subcommand and +// (internal/provider.dashboardResource, cairnobs_dashboard), reusing the +// exact same JSON contract cli/cmd/cairnobsctl's "apply" subcommand and // web's dashboard export button already use against // api/dashboards.Handler -- "one JSON contract, multiple callers" is a // design decision made back in Phase 3 (see cli/README.md), this @@ -21,7 +21,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/providerserver" - "github.com/sentry/sentry/terraform/internal/provider" + "github.com/cairnobs/cairnobs/terraform/internal/provider" ) // version is overridden at build time via -ldflags, same convention @@ -41,7 +41,7 @@ func main() { // required by the protocol even before actual registry // publication, since local dev overrides // (~/.terraformrc dev_overrides) key on this same address. - Address: "registry.terraform.io/sentry/sentry", + Address: "registry.terraform.io/cairnobs/cairnobs", Debug: debug, }) if err != nil { diff --git a/transport/Dockerfile b/transport/Dockerfile index 064af86..ed9db07 100644 --- a/transport/Dockerfile +++ b/transport/Dockerfile @@ -1,7 +1,7 @@ # Built FROM the Redpanda image so `rpk` is already present -- no need for # a separate client install, and no Docker socket access needed since # provisioning happens over the network, not via `docker exec`. -# docker build -f transport/Dockerfile -t sentry-transport-provision transport/ +# docker build -f transport/Dockerfile -t cairnobs-transport-provision transport/ FROM docker.redpanda.com/redpandadata/redpanda:v24.2.7 COPY provision-topics.sh /provision-topics.sh ENTRYPOINT ["/provision-topics.sh"] diff --git a/transport/docker-compose.yml b/transport/docker-compose.yml index 3bf1a2b..1a10c4a 100644 --- a/transport/docker-compose.yml +++ b/transport/docker-compose.yml @@ -12,7 +12,7 @@ services: redpanda: image: docker.redpanda.com/redpandadata/redpanda:v24.2.7 - container_name: sentry-redpanda + container_name: cairnobs-redpanda command: - redpanda - start diff --git a/web/Dockerfile b/web/Dockerfile index 60d2735..3bf0ab1 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -1,6 +1,6 @@ # Build context can be just web/ (unlike agent/ingest/api, this doesn't # need /proto): -# docker build -f web/Dockerfile -t sentry-web web/ +# docker build -f web/Dockerfile -t cairnobs-web web/ FROM node:22-alpine AS builder WORKDIR /src diff --git a/web/README.md b/web/README.md index dad371c..8c6ce67 100644 --- a/web/README.md +++ b/web/README.md @@ -34,8 +34,8 @@ npm run preview # serve the static build locally to sanity-check it ``` ```sh -docker build -f Dockerfile -t sentry-web . # context is web/, not the repo root -docker run -p 3000:3000 sentry-web +docker build -f Dockerfile -t cairnobs-web . # context is web/, not the repo root +docker run -p 3000:3000 cairnobs-web ``` ## Tenant picker (Phase 4) @@ -44,7 +44,7 @@ docker run -p 3000:3000 sentry-web clicking around the app -- `enterprise-auth`'s `internal/loginhandler` redirects a browser here after an SSO login resolves to more than one `tenant_memberships` row (see that package's doc comment), carrying a -short-lived `sentry_pending_login` cookie instead of a real session. The +short-lived `cairnobs_pending_login` cookie instead of a real session. The page calls `GET /auth/memberships` to list the choices, and `POST /auth/select-tenant` on a click, both via `fetch(..., {credentials: 'include'})` (`$lib/api.ts`'s diff --git a/web/src/app.html b/web/src/app.html index 2333503..cbc3e98 100644 --- a/web/src/app.html +++ b/web/src/app.html @@ -13,11 +13,11 @@ // $lib/density.ts; see those for the source of truth. (function () { try { - var theme = localStorage.getItem('sentry.theme'); + var theme = localStorage.getItem('cairnobs.theme'); if (theme === 'dark' || theme === 'light') { document.documentElement.setAttribute('data-theme', theme); } - if (localStorage.getItem('sentry.density') === 'compact') { + if (localStorage.getItem('cairnobs.density') === 'compact') { document.documentElement.classList.add('density-compact'); } } catch (e) { diff --git a/web/src/lib/api.ts b/web/src/lib/api.ts index 96e530e..ab511c1 100644 --- a/web/src/lib/api.ts +++ b/web/src/lib/api.ts @@ -285,7 +285,7 @@ export async function getAuthFeatures(): Promise { // // The two calls below are the reason getAuthFeatures above doesn't send // credentials but these do: they carry the short-lived -// sentry_pending_login cookie enterprise-auth's finishLogin sets when an +// cairnobs_pending_login cookie enterprise-auth's finishLogin sets when an // identity resolves to more than one tenant_memberships row (see // enterprise/internal/loginhandler's package doc comment), and // selectTenant's response sets the real session cookie. Both require @@ -716,8 +716,8 @@ export function issueAgentCommand(host: string, command: 'restart'): Promise { - const result = await runQuery('sentry.metrics=true | stats count by host', 'spl'); + const result = await runQuery('cairnobs.metrics=true | stats count by host', 'spl'); const hostIdx = result.columns.indexOf('host'); const countIdx = result.columns.indexOf('count'); return result.rows.map((r) => ({ host: String(r[hostIdx]), sampleCount: Number(r[countIdx]) })); @@ -763,7 +763,7 @@ export type HostMetrics = { // attribute columns the same way filtering does. export async function getHostMetrics(host: string): Promise { const result = await runQuery( - `host="${host}" sentry.metrics=true | sort -timestamp | head 1`, + `host="${host}" cairnobs.metrics=true | sort -timestamp | head 1`, 'spl' ); if (result.rows.length === 0) return null; diff --git a/web/src/lib/assets/favicon.svg b/web/src/lib/assets/favicon.svg index cc5dc66..2f6a1e8 100644 --- a/web/src/lib/assets/favicon.svg +++ b/web/src/lib/assets/favicon.svg @@ -1 +1,9 @@ -svelte-logo \ No newline at end of file + + + + + + + + + diff --git a/web/src/lib/assets/logo-horizontal-dark.svg b/web/src/lib/assets/logo-horizontal-dark.svg new file mode 100644 index 0000000..f822879 --- /dev/null +++ b/web/src/lib/assets/logo-horizontal-dark.svg @@ -0,0 +1,12 @@ + + + + + + + + + cairn + obs + + diff --git a/web/src/lib/assets/logo-stacked-dark.svg b/web/src/lib/assets/logo-stacked-dark.svg new file mode 100644 index 0000000..3ba5c74 --- /dev/null +++ b/web/src/lib/assets/logo-stacked-dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + cairn obs + diff --git a/web/src/lib/charts/drilldown.ts b/web/src/lib/charts/drilldown.ts index b4717c7..edf6f42 100644 --- a/web/src/lib/charts/drilldown.ts +++ b/web/src/lib/charts/drilldown.ts @@ -64,5 +64,5 @@ export function drillDownUrl(target: DrillDownTarget): string { const params = new URLSearchParams({ q: target.query }); if (target.earliest) params.set('earliest', target.earliest); if (target.latest) params.set('latest', target.latest); - return `/?${params.toString()}`; + return `/search?${params.toString()}`; } diff --git a/web/src/lib/charts/setup.ts b/web/src/lib/charts/setup.ts index 396e317..9bb9288 100644 --- a/web/src/lib/charts/setup.ts +++ b/web/src/lib/charts/setup.ts @@ -2,7 +2,7 @@ // would ship every chart type/component ECharts has ever shipped, // against the whole reason it was picked over hand-rolled D3 for the // bundle-size tradeoff (see the Phase 5 charting-library review). This -// registers only what Sentry's five chart types actually use: line/bar +// registers only what Cairn OBS's five chart types actually use: line/bar // (time-series, stacked bar, top-N, the single-stat sparkline) and // heatmap, plus tooltip/legend/grid/dataZoom/visualMap and the canvas // renderer. Imported once, here, not per-component -- echarts.use() is diff --git a/web/src/lib/components/CommandPalette.svelte b/web/src/lib/components/CommandPalette.svelte index d9dfc2b..cca3b72 100644 --- a/web/src/lib/components/CommandPalette.svelte +++ b/web/src/lib/components/CommandPalette.svelte @@ -7,7 +7,7 @@ type Item = { id: string; label: string; hint: string; go: () => void }; const staticItems: Item[] = [ - { id: 'nav-search', label: 'Search', hint: 'Go to', go: () => goto('/') }, + { id: 'nav-search', label: 'Search', hint: 'Go to', go: () => goto('/search') }, { id: 'nav-dashboards', label: 'Dashboards', hint: 'Go to', go: () => goto('/dashboards') }, { id: 'nav-alerts', label: 'Alerts', hint: 'Go to', go: () => goto('/alerts') }, { id: 'nav-data-sources', label: 'Data Sources', hint: 'Go to', go: () => goto('/data-sources') }, diff --git a/web/src/lib/components/NavSidebar.svelte b/web/src/lib/components/NavSidebar.svelte index 6f922e6..a92a549 100644 --- a/web/src/lib/components/NavSidebar.svelte +++ b/web/src/lib/components/NavSidebar.svelte @@ -11,6 +11,7 @@ } from '$lib/api'; import { getTheme, setTheme, type Theme } from '$lib/theme.svelte'; import { getDensity, toggleDensity } from '$lib/density.svelte'; + import logo from '$lib/assets/logo-horizontal-dark.svg'; let { onOpenPalette, @@ -19,7 +20,7 @@ }: { onOpenPalette: () => void; mobileOpen?: boolean; onCloseMobile?: () => void } = $props(); const baseNavItems = [ - { href: '/', label: 'Search', icon: '◇' }, + { href: '/search', label: 'Search', icon: '◇' }, { href: '/dashboards', label: 'Dashboards', icon: '▤' }, { href: '/alerts', label: 'Alerts', icon: '▲' }, { href: '/data-sources', label: 'Data Sources', icon: '◈' }, @@ -82,8 +83,9 @@