Files
o11y/pkg/sqlmigration/061_migrate_rbac_to_authz.go
zeekayandGitHub babbdec4b8 debrand: SigNoz -> O11y across the tree (+ otel-collector v0.144.7, schema cutover migration) (#28)
* debrand: signoz/SigNoz/SIGNOZ -> o11y/O11y/O11Y across the tree

Rename all SigNoz *branding* to O11y in file contents, file/dir names,
package names, env vars, config keys, comments, docs, SDK code, and the
ClickHouse schema identifiers the querier reads.

Collector dependency:
- Repoint github.com/hanzoai/signoz-otel-collector ->
  github.com/hanzoai/otel-collector and bump v0.144.6 -> v0.144.7
  (go.mod + go.sum reconciled via go mod tidy). Internal package paths
  also debranded upstream: signozschemamigrator -> o11yschemamigrator,
  signozlogspipelineprocessor -> o11ylogspipelineprocessor.
- Fix: v0.144.7 dropped otelconst.DistributedFieldKeysTable from the
  collector's public constants; pin FieldKeysTable = "distributed_field_keys"
  locally in pkg/telemetrymetadata/tables.go.

Package/dir renames (package decl + all importers):
  pkg/apiserver/signozapiserver -> o11yapiserver, plus signozalertmanager,
  signozauthzapi, signozglobal, signozquerier, signozruler, and pkg/signoz -> pkg/o11y.

Schema (read plane): signoz_traces/metrics/logs/metadata/analytics/meter and
signoz_index_v3/index_v2/error_index_v2/spans (+ distributed_*) -> o11y_*.
Data-preserving cutover migration added:
  deploy/clickhouse/migrations/0001_rename_signoz_to_o11y.sql
  (metadata-only RENAME DATABASE/TABLE, no drop/recreate).

Preserved (attribution / external / wire contracts):
- LICENSE + NOTICE verbatim (incl. "software from SigNoz", "Copyright ... SigNoz Inc.").
- Upstream github.com/SigNoz/* repo URLs + @SigNoz/* CODEOWNERS teams.
- Billing resource-attribute regex "signoz.workspace.*" (external wire contract).

go build ./pkg/... ./cmd/... = 0; gofmt clean; renamed+schema pkg tests pass.

* o11y: bump collector to v0.144.8 (o11y_* physical schema writer) + lockstep deploy plan

Collector v0.144.8's schema-migrator + exporters now CREATE/WRITE the same
o11y_* physical databases/tables the querier reads and the RENAME migration
(0001_rename_signoz_to_o11y.sql) targets.

Cross-checked byte-identical: writer DB set == migration target DB set (6);
writer prefixed-table set == migration target table set (8); o11y querier
reads and cloud reads are subsets of that set. Zero table-name mismatches
between writer, readers, and migration.

Collector go.mod unchanged between v0.144.7 and v0.144.8 (identical go.mod
hash) — pure source rename, no module-graph change.

Adds deploy/clickhouse/DEPLOY_signoz_to_o11y_cutover.md: the lockstep order
(collector v0.144.8 -> RENAME migration -> o11y+cloud readers) with a
fresh/scratch-ClickHouse pre-flight (create via migrator, emit trace+metric,
query back) and rollback. Ships together or telemetry blackholes.

go build ./pkg/... ./cmd/community = 0.
2026-07-09 13:08:55 -07:00

226 lines
6.3 KiB
Go

package sqlmigration
import (
"context"
"database/sql"
"time"
"github.com/hanzoai/o11y/pkg/errors"
"github.com/hanzoai/o11y/pkg/factory"
"github.com/hanzoai/o11y/pkg/sqlstore"
"github.com/hanzoai/o11y/pkg/types/coretypes"
"github.com/oklog/ulid/v2"
"github.com/uptrace/bun"
"github.com/uptrace/bun/dialect"
"github.com/uptrace/bun/migrate"
)
type migrateRbacToAuthz struct {
sqlstore sqlstore.SQLStore
}
var (
existingRoleToO11yManagedRoleMap = map[string]string{
"ADMIN": "o11y-admin",
"EDITOR": "o11y-editor",
"VIEWER": "o11y-viewer",
}
)
func NewMigrateRbacToAuthzFactory(sqlstore sqlstore.SQLStore) factory.ProviderFactory[SQLMigration, Config] {
return factory.NewProviderFactory(factory.MustNewName("migrate_rbac_to_authz"), func(ctx context.Context, ps factory.ProviderSettings, c Config) (SQLMigration, error) {
return newMigrateRbacToAuthz(ctx, ps, c, sqlstore)
})
}
func newMigrateRbacToAuthz(_ context.Context, _ factory.ProviderSettings, _ Config, sqlstore sqlstore.SQLStore) (SQLMigration, error) {
return &migrateRbacToAuthz{
sqlstore: sqlstore,
}, nil
}
func (migration *migrateRbacToAuthz) Register(migrations *migrate.Migrations) error {
if err := migrations.Register(migration.Up, migration.Down); err != nil {
return err
}
return nil
}
func (migration *migrateRbacToAuthz) Up(ctx context.Context, db *bun.DB) error {
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer func() {
_ = tx.Rollback()
}()
// for upgrades from version where authz service wasn't introduced the store won't be present, hence we need to ensure store exists.
var storeID string
err = tx.QueryRowContext(ctx, `SELECT id FROM store WHERE name = ? LIMIT 1`, "o11y").Scan(&storeID)
if err != nil && err != sql.ErrNoRows {
return err
}
if storeID == "" {
// based on openfga ids to avoid any scan issues.
// ref: https://github.com/openfga/openfga/blob/main/pkg/server/commands/create_store.go#L45
storeID = ulid.Make().String()
_, err := tx.ExecContext(ctx, `INSERT INTO store (id, name, created_at, updated_at) VALUES (?, ?, ?, ?)`, storeID, "o11y", time.Now().UTC(), time.Now().UTC())
if err != nil {
return err
}
}
// fetch all the orgs for which we need to insert user role grant tuples.
orgIDs := []string{}
rows, err := tx.QueryContext(ctx, `SELECT id FROM organizations`)
if err != nil {
return err
}
defer rows.Close()
for rows.Next() {
var orgID string
if err := rows.Scan(&orgID); err != nil {
return err
}
orgIDs = append(orgIDs, orgID)
}
type tuple struct {
OrgID string
Type string
ID string
RoleName string
}
tuples := []tuple{}
for _, orgID := range orgIDs {
userRows, err := tx.QueryContext(ctx, `
SELECT id, role FROM users WHERE org_id = ?`, orgID)
if err != nil {
return err
}
defer userRows.Close()
for userRows.Next() {
var id, role string
if err := userRows.Scan(&id, &role); err != nil {
return err
}
managedRole, ok := existingRoleToO11yManagedRoleMap[role]
if !ok {
return errors.Newf(errors.TypeInternal, errors.CodeInternal, "invalid role assignment: %s for user_id: %s", role, id)
}
tuples = append(tuples, tuple{
OrgID: orgID,
ID: id,
Type: "user",
RoleName: managedRole,
})
}
tuples = append(tuples, tuple{
OrgID: orgID,
ID: coretypes.AnonymousUser.StringValue(),
Type: "anonymous",
RoleName: "o11y-anonymous",
})
}
_, err = tx.ExecContext(ctx, `DELETE FROM tuple`)
if err != nil {
return err
}
_, err = tx.ExecContext(ctx, `DELETE FROM changelog`)
if err != nil {
return err
}
for _, tuple := range tuples {
// based on openfga tuple and changelog id's are same for writes.
// ref: https://github.com/openfga/openfga/blob/main/pkg/storage/sqlite/sqlite.go#L467
entropy := ulid.DefaultEntropy()
now := time.Now().UTC()
tupleID := ulid.MustNew(ulid.Timestamp(now), entropy).String()
if migration.sqlstore.BunDB().Dialect().Name() == dialect.PG {
result, err := tx.ExecContext(ctx, `
INSERT INTO tuple (store, object_type, object_id, relation, _user, user_type, ulid, inserted_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT (store, object_type, object_id, relation, _user) DO NOTHING`,
storeID, "role", "organization/"+tuple.OrgID+"/role/"+tuple.RoleName, "assignee", tuple.Type+":organization/"+tuple.OrgID+"/"+tuple.Type+"/"+tuple.ID, "user", tupleID, now,
)
if err != nil {
return err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
continue
}
_, err = tx.ExecContext(ctx, `
INSERT INTO changelog (store, object_type, object_id, relation, _user, operation, ulid, inserted_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT (store, ulid, object_type) DO NOTHING`,
storeID, "role", "organization/"+tuple.OrgID+"/role/"+tuple.RoleName, "assignee", tuple.Type+":organization/"+tuple.OrgID+"/"+tuple.Type+"/"+tuple.ID, "TUPLE_OPERATION_WRITE", tupleID, now,
)
if err != nil {
return err
}
} else {
result, err := tx.ExecContext(ctx, `
INSERT INTO tuple (store, object_type, object_id, relation, user_object_type, user_object_id, user_relation, user_type, ulid, inserted_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT (store, object_type, object_id, relation, user_object_type, user_object_id, user_relation) DO NOTHING`,
storeID, "role", "organization/"+tuple.OrgID+"/role/"+tuple.RoleName, "assignee", tuple.Type, "organization/"+tuple.OrgID+"/"+tuple.Type+"/"+tuple.ID, "", "user", tupleID, now,
)
if err != nil {
return err
}
rowsAffected, err := result.RowsAffected()
if err != nil {
return err
}
if rowsAffected == 0 {
continue
}
_, err = tx.ExecContext(ctx, `
INSERT INTO changelog (store, object_type, object_id, relation, user_object_type, user_object_id, user_relation, operation, ulid, inserted_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT (store, ulid, object_type) DO NOTHING`,
storeID, "role", "organization/"+tuple.OrgID+"/role/"+tuple.RoleName, "assignee", tuple.Type, "organization/"+tuple.OrgID+"/"+tuple.Type+"/"+tuple.ID, "", 0, tupleID, now,
)
if err != nil {
return err
}
}
}
err = tx.Commit()
if err != nil {
return err
}
return nil
}
func (migration *migrateRbacToAuthz) Down(context.Context, *bun.DB) error {
return nil
}