cmd/tools: remove migration tool code for generating missing salt

Remove generate-missing-project-salt migration tool code and related
tests. This migration has already been run and this code is no longer
needed.

Issue https://github.com/storj/storj-private/issues/163

Change-Id: I4e36dcd95a07c5305c597113a7fd08148e100ccc
This commit is contained in:
Lizzy Thomson 2023-03-09 12:11:12 -07:00 committed by Storj Robot
parent 0559d19db5
commit 7e2d98988b
5 changed files with 0 additions and 361 deletions

View File

@ -1,93 +0,0 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
package main
import (
"context"
"errors"
pgx "github.com/jackc/pgx/v4"
"github.com/spacemonkeygo/monkit/v3"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
"github.com/zeebo/errs"
"go.uber.org/zap"
"storj.io/private/process"
)
var mon = monkit.Package()
var (
rootCmd = &cobra.Command{
Use: "generate-missing-project-salt",
Short: "generate-missing-project-salt",
}
runCmd = &cobra.Command{
Use: "run",
Short: "run generate-missing-project-salt",
RunE: run,
}
config Config
)
func init() {
rootCmd.AddCommand(runCmd)
config.BindFlags(runCmd.Flags())
}
// Config defines configuration for migration.
type Config struct {
SatelliteDB string
Limit int
MaxUpdates int
}
// BindFlags adds bench flags to the the flagset.
func (config *Config) BindFlags(flag *flag.FlagSet) {
flag.StringVar(&config.SatelliteDB, "satellitedb", "", "connection URL for satelliteDB")
flag.IntVar(&config.Limit, "limit", 1000, "number of updates to perform at once")
flag.IntVar(&config.MaxUpdates, "max-updates", 0, "max number of updates to perform on each table")
}
// VerifyFlags verifies whether the values provided are valid.
func (config *Config) VerifyFlags() error {
var errlist errs.Group
if config.SatelliteDB == "" {
errlist.Add(errors.New("flag '--satellitedb' is not set"))
}
return errlist.Err()
}
func run(cmd *cobra.Command, args []string) error {
if err := config.VerifyFlags(); err != nil {
return err
}
ctx, _ := process.Ctx(cmd)
log := zap.L()
return GenerateMissingProjectSalt(ctx, log, config)
}
func main() {
process.Exec(rootCmd)
}
// GenerateMissingProjectSalt updates projects salt column where salt is null.
func GenerateMissingProjectSalt(ctx context.Context, log *zap.Logger, config Config) (err error) {
defer mon.Task()(&ctx)(&err)
conn, err := pgx.Connect(ctx, config.SatelliteDB)
if err != nil {
return errs.New("unable to connect %q: %w", config.SatelliteDB, err)
}
defer func() {
err = errs.Combine(err, conn.Close(ctx))
}()
return GenerateMissingSalt(ctx, log, conn, config)
}

View File

@ -1,130 +0,0 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
package main_test
import (
"bytes"
"context"
"crypto/sha256"
"strings"
"testing"
pgx "github.com/jackc/pgx/v4"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"go.uber.org/zap/zaptest"
"storj.io/common/testcontext"
"storj.io/common/testrand"
"storj.io/common/uuid"
"storj.io/private/dbutil"
"storj.io/private/dbutil/tempdb"
migrator "storj.io/storj/cmd/tools/generate-missing-project-salt"
"storj.io/storj/satellite"
"storj.io/storj/satellite/console"
"storj.io/storj/satellite/satellitedb/satellitedbtest"
)
// Test salt column is updated correctly.
func TestGenerateMissingSaltTest(t *testing.T) {
t.Parallel()
prepare := func(t *testing.T, ctx *testcontext.Context, rawDB *dbutil.TempDatabase, db satellite.DB, conn *pgx.Conn, log *zap.Logger) (projectsIDs []uuid.UUID, shouldUpdate int) {
myProject1, err := db.Console().Projects().Insert(ctx, &console.Project{
Name: "test1",
Description: "test1",
OwnerID: testrand.UUID(),
})
require.NoError(t, err)
projectsIDs = append(projectsIDs, myProject1.ID)
err = db.Console().Projects().TestNullifySalt(ctx, myProject1.ID)
require.NoError(t, err)
salt1, err := db.Console().Projects().TestGetSalt(ctx, myProject1.ID)
require.NoError(t, err)
require.Equal(t, len(salt1), 0)
shouldUpdate++
// Project "test2" should have a populated salt column and should not get updated.
myProject2, err := db.Console().Projects().Insert(ctx, &console.Project{
Name: "test2",
Description: "test2",
OwnerID: testrand.UUID(),
})
require.NoError(t, err)
projectsIDs = append(projectsIDs, myProject2.ID)
salt2, err := db.Console().Projects().TestGetSalt(ctx, myProject2.ID)
require.NoError(t, err)
require.NotNil(t, salt2)
return projectsIDs, shouldUpdate
}
check := func(t *testing.T, ctx context.Context, db satellite.DB, projectsIDs []uuid.UUID, shouldUpdate int) {
var updated int
var notUpdated int
for _, p := range projectsIDs {
saltdb, err := db.Console().Projects().TestGetSalt(ctx, p)
require.NoError(t, err)
idHash := sha256.Sum256(p[:])
salt := idHash[:]
// if the salt column is the hashed project ID, it means we migrated that row
if bytes.Equal(salt, saltdb) {
updated++
} else {
notUpdated++
}
}
require.Equal(t, shouldUpdate, updated)
require.Equal(t, len(projectsIDs)-shouldUpdate, notUpdated)
}
test(t, prepare, migrator.GenerateMissingSalt, check, &migrator.Config{
Limit: 2,
})
}
func test(t *testing.T,
prepare func(t *testing.T, ctx *testcontext.Context, rawDB *dbutil.TempDatabase, db satellite.DB, conn *pgx.Conn, log *zap.Logger) (projectsIDs []uuid.UUID, shouldUpdate int),
migrate func(ctx context.Context, log *zap.Logger, conn *pgx.Conn, config migrator.Config) (err error),
check func(t *testing.T, ctx context.Context, db satellite.DB, projectsIDs []uuid.UUID, shouldUpdate int), config *migrator.Config) {
log := zaptest.NewLogger(t)
for _, satelliteDB := range satellitedbtest.Databases() {
satelliteDB := satelliteDB
t.Run(satelliteDB.Name, func(t *testing.T) {
t.Parallel()
ctx := testcontext.New(t)
schemaSuffix := satellitedbtest.SchemaSuffix()
schema := satellitedbtest.SchemaName(t.Name(), "category", 0, schemaSuffix)
tempDB, err := tempdb.OpenUnique(ctx, satelliteDB.MasterDB.URL, schema)
require.NoError(t, err)
db, err := satellitedbtest.CreateMasterDBOnTopOf(ctx, log, tempDB, "generate-missing-project-salt")
require.NoError(t, err)
defer ctx.Check(db.Close)
err = db.Testing().TestMigrateToLatest(ctx)
require.NoError(t, err)
mConnStr := strings.Replace(tempDB.ConnStr, "cockroach", "postgres", 1)
conn, err := pgx.Connect(ctx, mConnStr)
require.NoError(t, err)
defer func() {
require.NoError(t, conn.Close(ctx))
}()
projectsIDs, shouldUpdate := prepare(t, ctx, tempDB, db, conn, log)
err = migrate(ctx, log, conn, *config)
require.NoError(t, err)
check(t, ctx, db, projectsIDs, shouldUpdate)
})
}
}

View File

@ -1,102 +0,0 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
package main
import (
"context"
"crypto/sha256"
pgx "github.com/jackc/pgx/v4"
"github.com/zeebo/errs"
"go.uber.org/zap"
"storj.io/private/dbutil/cockroachutil"
"storj.io/private/dbutil/pgutil"
)
// GenerateMissingSalt updates the salt column in projects table to add SHA-256 hash of the project ID where salt is NULL.
func GenerateMissingSalt(ctx context.Context, log *zap.Logger, conn *pgx.Conn, config Config) (err error) {
defer mon.Task()(&ctx)(&err)
lastID := []byte{}
var total int
var rowsFound bool
for {
rowsFound = false
idsToUpdate := struct {
ids [][]byte
salt [][]byte
}{}
err = func() error {
rows, err := conn.Query(ctx, `
SELECT id FROM projects
WHERE salt IS NULL
AND id > $1
ORDER BY id
LIMIT $2
`, lastID, config.Limit)
if err != nil {
return errs.New("error select ids for update: %w", err)
}
defer rows.Close()
for rows.Next() {
rowsFound = true
var id []byte
err = rows.Scan(&id)
if err != nil {
return errs.New("error scanning results from select: %w", err)
}
idHash := sha256.Sum256(id)
salt := idHash[:]
idsToUpdate.salt = append(idsToUpdate.salt, salt)
idsToUpdate.ids = append(idsToUpdate.ids, id)
lastID = id
}
return rows.Err()
}()
if err != nil {
return err
}
if !rowsFound {
break
}
var updated int
for {
row := conn.QueryRow(ctx,
`WITH to_update AS (
SELECT unnest($1::bytea[]) as id,
unnest($2::bytea[]) as salt
),
updated as (
UPDATE projects
SET salt = to_update.salt
FROM to_update
WHERE projects.id = to_update.id
RETURNING 1
)
SELECT count(*)
FROM updated;
`, pgutil.ByteaArray(idsToUpdate.ids), pgutil.ByteaArray(idsToUpdate.salt))
err := row.Scan(&updated)
if err != nil {
if cockroachutil.NeedsRetry(err) {
continue
} else if errs.Is(err, pgx.ErrNoRows) {
break
}
return errs.New("error updating projects %w", err)
}
break
}
total += updated
log.Info("batch update complete", zap.Int("rows updated", updated), zap.Binary("last id", lastID))
}
log.Info("projects migration complete", zap.Int("total rows updated", total))
return nil
}

View File

@ -54,13 +54,6 @@ type Projects interface {
// UpdateUsageLimits is a method for updating project's usage limits.
UpdateUsageLimits(ctx context.Context, id uuid.UUID, limits UsageLimits) error
// TestGetSalt returns the value (whether null or hashed project ID) from project's salt column in db.
TestGetSalt(ctx context.Context, id uuid.UUID) ([]byte, error)
// TestNullifySalt is a temporary method for nullifying the salt column
// for testing a migration tool (TODO lizzy delete after migration).
TestNullifySalt(ctx context.Context, id uuid.UUID) error
}
// UsageLimitsConfig is a configuration struct for default per-project usage limits.

View File

@ -103,20 +103,6 @@ func (projects *projects) GetSalt(ctx context.Context, id uuid.UUID) (salt []byt
return salt, nil
}
// TestGetSalt returns the project's salt column value from the db.
func (projects *projects) TestGetSalt(ctx context.Context, id uuid.UUID) (salt []byte, err error) {
defer mon.Task()(&ctx)(&err)
res, err := projects.db.Get_Project_Salt_By_Id(ctx, dbx.Project_Id(id[:]))
if err != nil {
return nil, err
}
salt = res.Salt
return salt, nil
}
// GetByPublicID is a method for querying project from the database by public_id.
func (projects *projects) GetByPublicID(ctx context.Context, publicID uuid.UUID) (_ *console.Project, err error) {
defer mon.Task()(&ctx)(&err)
@ -472,18 +458,3 @@ func (projects *projects) UpdateUsageLimits(ctx context.Context, id uuid.UUID, l
)
return err
}
// TestNullifySalt is a temporary method for nullifying the salt column
// for testing a migration tool (TODO lizzy delete after migration).
func (projects *projects) TestNullifySalt(ctx context.Context, id uuid.UUID) (err error) {
defer mon.Task()(&ctx)(&err)
var salt []byte
row := projects.sdb.QueryRow(ctx, `UPDATE projects SET salt = NULL WHERE id = $1 RETURNING salt`, id)
err = row.Scan(&salt)
if err != nil {
return errs.New("error scanning results: %w", err)
}
return err
}