Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
7edd1aa
Add Blob store to Postgres
Mar 14, 2023
b77920a
Add utilities to pass transactions through contexts
Mar 14, 2023
7d4d493
dont forget the proto file
Mar 14, 2023
46c4217
Merge branch 'master' into cgorman-tx-context
c-du Apr 28, 2023
9ae1ca7
Merge branch 'cgorman-tx-context' into cgorman-blob-store
c-du Apr 28, 2023
92d1c0e
stage
c-du Apr 28, 2023
d40b4ec
stage
c-du Apr 29, 2023
4384c9a
Add delete
c-du May 1, 2023
aa2197e
Merge branch 'master' into cgorman-tx-context
c-du May 1, 2023
fe09a82
Merge branch 'cong/tx' into cong/blobstore
c-du May 1, 2023
8d2a24b
regen
c-du May 1, 2023
8df6ccc
Merge branch 'master' into cong/blobstore
c-du May 9, 2023
96cfee9
Address review comments
c-du May 9, 2023
13c3efd
no blob
c-du May 10, 2023
c962711
Merge branch 'cong/blobstore' into cong/vuldef
c-du May 10, 2023
f9d3511
stage
c-du May 10, 2023
e1129f4
stage
c-du May 10, 2023
ee06bc3
Merge branch 'master' into cong/vuldef
c-du May 10, 2023
44e45b5
stage
c-du May 10, 2023
c892183
cherry-pick
c-du May 10, 2023
c0434f2
Merge branch 'master' into cong/vuldef
c-du May 10, 2023
007feb1
Use blobstore for scanner definitions
c-du May 10, 2023
79142a5
Merge branch 'master' of github.com:stackrox/stackrox
c-du May 11, 2023
864cca8
Merge branch 'master' into cong/vuldef
c-du May 11, 2023
1e36fc3
stage
c-du May 11, 2023
1312343
first break it
c-du May 11, 2023
59bb9eb
Merge branch 'master' into cong/vuldef
c-du May 11, 2023
e57c2f7
security
c-du May 15, 2023
f254423
revert sneak
c-du May 15, 2023
b2f6f6b
revert sneak
c-du May 15, 2023
3920356
fix
c-du May 15, 2023
595d12b
stage
c-du May 16, 2023
6b52d7c
stage
c-du May 16, 2023
375ad88
stage
c-du May 16, 2023
436ecc0
stage
c-du May 17, 2023
9a50c29
stage
c-du May 17, 2023
aa19928
stage
c-du May 17, 2023
853ed61
stage
c-du May 17, 2023
4aebc48
stage
c-du May 17, 2023
921254e
stage
c-du May 17, 2023
28b45d1
stage
c-du May 17, 2023
c9c7c4e
Migrate persistent data #1 - scanner definition
c-du May 15, 2023
5886e46
Merge branch 'master' into cong/vuldef
c-du May 17, 2023
6ad546b
Merge branch 'cong/vuldef' into cong/vul-test
c-du May 17, 2023
c30a3b0
Merge branch 'cong/vul-test' into cong/vul-migrate
c-du May 17, 2023
bea7011
merge and style
c-du May 17, 2023
d37f371
small review first
c-du May 17, 2023
84b4536
Merge branch 'cong/vuldef' into cong/vul-migrate
c-du May 17, 2023
5a803d0
Resolve review comments
c-du May 17, 2023
dc5d829
Merge branch 'cong/vuldef' into cong/vul-migrate
c-du May 17, 2023
f7190ff
add migration
c-du May 17, 2023
09b16a5
review comments
c-du May 18, 2023
c0346c0
close
c-du May 18, 2023
60e8a15
review
c-du May 18, 2023
0799e65
size and minor change
c-du May 18, 2023
9c6812d
Merge branch 'cong/vuldef' into cong/vul-migrate
c-du May 18, 2023
8fe29a9
Merge branch 'master' into cong/vuldef
c-du May 18, 2023
89b1199
Merge branch 'cong/vuldef' into cong/vul-migrate
c-du May 18, 2023
2bb5551
Resolve review comment
c-du May 19, 2023
e40addf
Merge branch 'master' into cong/vul-migrate
c-du May 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions migrator/migrations/m_180_to_m_181_move_to_blobstore/migration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package m180tom181

import (
"context"
"database/sql"
"os"

timestamp "github.com/gogo/protobuf/types"
"github.com/pkg/errors"
"github.com/stackrox/rox/generated/storage"
"github.com/stackrox/rox/migrator/migrations"
"github.com/stackrox/rox/migrator/migrations/m_180_to_m_181_move_to_blobstore/schema"
"github.com/stackrox/rox/migrator/types"
"github.com/stackrox/rox/pkg/logging"
"github.com/stackrox/rox/pkg/postgres/gorm/largeobject"
"github.com/stackrox/rox/pkg/postgres/pgutils"
"github.com/stackrox/rox/pkg/sac"
"github.com/stackrox/rox/pkg/utils"
"gorm.io/gorm"
)

const (
scannerDefBlobName = "/offline/scanner/scanner-defs.zip"
)

var (
scannerDefPath = "/var/lib/stackrox/scannerdefinitions/scanner-defs.zip"
)

var (
migration = types.Migration{
StartingSeqNum: 180,
VersionAfter: &storage.Version{SeqNum: 181},
Run: func(databases *types.Databases) error {
err := moveToBlobs(databases.GormDB)
if err != nil {
return errors.Wrap(err, "moving persistent files to blobs")
}
return nil
},
}
log = logging.LoggerForModule()
)

func moveToBlobs(db *gorm.DB) (err error) {
ctx := sac.WithAllAccess(context.Background())
db = db.WithContext(ctx).Table(schema.BlobsTableName)
pgutils.CreateTableFromModel(context.Background(), db, schema.CreateTableBlobsStmt)

tx := db.Begin(&sql.TxOptions{Isolation: sql.LevelRepeatableRead})
if err = moveScannerDefinitions(tx); err != nil {
result := tx.Rollback()
if result.Error != nil {
log.Warnf("failed to rollback with error %v", result.Error)
}
return errors.Wrap(err, "failed to move scanner definition to blob store.")
}

return tx.Commit().Error
}

func moveScannerDefinitions(tx *gorm.DB) error {
fd, err := os.Open(scannerDefPath)
if os.IsNotExist(err) {
return nil
}
if err != nil {
return errors.Wrapf(err, "failed to open %s", scannerDefPath)
}
defer utils.IgnoreError(fd.Close)
stat, err := fd.Stat()
if err != nil {
return err
}
if stat.IsDir() {
return nil
}
modTime, err := timestamp.TimestampProto(stat.ModTime())
if err != nil {
return errors.Wrapf(err, "invalid timestamp %v", stat.ModTime())
}

// Prepare blob
blob := &storage.Blob{
Name: scannerDefBlobName,
Length: stat.Size(),
LastUpdated: timestamp.TimestampNow(),
ModifiedTime: modTime,
}
los := largeobject.LargeObjects{DB: tx}

// Find the blob if it exists
var targets []schema.Blobs
result := tx.Limit(1).Where(&schema.Blobs{Name: scannerDefBlobName}).Find(&targets)
if result.Error != nil {
return result.Error
}

if len(targets) == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to check and distinguish in this case? Migrations only run once and we can be guaranteed that no object will exist in the database because all of this is wrapped in a txn. I think it's a fair assumption that this does not exist and that we will always need to add and create

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This could happen when the first migration fails maybe at migration 182 and then we fixed the problem somehow and customer tries again. All migrations should be re-entriable.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah good point, do you think it'd be generally worthwhile to write out the version after each sequence going forward? Not in this PR of course

blob.Oid, err = los.Create()
if err != nil {
return errors.Wrap(err, "failed to create large object")
}
} else {
// Update
existingBlob, err := schema.ConvertBlobToProto(&targets[0])
if err != nil {
return errors.Wrapf(err, "existing blob is not valid %+v", targets[0])
}
blob.Oid = existingBlob.Oid
}
blobModel, err := schema.ConvertBlobFromProto(blob)
if err != nil {
return errors.Wrapf(err, "failed to convert blob to blob model %+v", blob)
}
tx = tx.FirstOrCreate(blobModel)
if tx.Error != nil {
return errors.Wrap(tx.Error, "failed to create blob metadata")
}
return los.Upsert(blob.Oid, fd)
}

func init() {
migrations.MustRegisterMigration(migration)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
//go:build sql_integration

package m180tom181

import (
"bytes"
"crypto/rand"
"io"
"os"
"testing"

"github.com/stackrox/rox/migrator/migrations/m_180_to_m_181_move_to_blobstore/schema"
pghelper "github.com/stackrox/rox/migrator/migrations/postgreshelper"
"github.com/stackrox/rox/pkg/postgres/gorm/largeobject"
"github.com/stackrox/rox/pkg/postgres/pgutils"
"github.com/stretchr/testify/suite"
)

type blobMigrationTestSuite struct {
suite.Suite

db *pghelper.TestPostgres
}

func TestMigration(t *testing.T) {
suite.Run(t, new(blobMigrationTestSuite))
}

func (s *blobMigrationTestSuite) SetupTest() {
s.db = pghelper.ForT(s.T(), true)
}

func (s *blobMigrationTestSuite) TearDownTest() {
s.db.Teardown(s.T())
}

func (s *blobMigrationTestSuite) TestMigration() {
// Nothing to migrate
s.Require().NoError(moveToBlobs(s.db.GetGormDB()))

// Prepare persistent file
size := 90000
randomData := make([]byte, size)
_, err := rand.Read(randomData)
s.Require().NoError(err)
reader := bytes.NewBuffer(randomData)

file, err := os.CreateTemp("", "move-blob")
s.Require().NoError(err)
defer func() {
s.NoError(file.Close())
s.NoError(os.Remove(file.Name()))
}()
scannerDefPath = file.Name()
n, err := io.Copy(file, reader)
s.Require().NoError(err)

s.Require().EqualValues(size, n)
fileInfo, err := file.Stat()
s.Require().NoError(err)

// Migrate
s.Require().NoError(moveToBlobs(s.db.GetGormDB()))

// Verify Blob
blobModel := &schema.Blobs{Name: scannerDefBlobName}
s.Require().NoError(s.db.GetGormDB().First(&blobModel).Error)

blob, err := schema.ConvertBlobToProto(blobModel)
s.Require().NoError(err)
s.Equal(scannerDefBlobName, blob.GetName())
s.EqualValues(size, blob.GetLength())

modTime := pgutils.NilOrTime(blob.GetModifiedTime())
s.Equal(fileInfo.ModTime().UTC(), modTime.UTC())

// Verify Data
buf := bytes.NewBuffer([]byte{})

tx := s.db.GetGormDB().Begin()
s.Require().NoError(err)
los := &largeobject.LargeObjects{DB: tx}
s.Require().NoError(los.Get(blob.Oid, buf))
s.Equal(len(randomData), buf.Len())
s.Equal(randomData, buf.Bytes())
s.NoError(tx.Commit().Error)

// Test re-entry
s.Require().NoError(moveToBlobs(s.db.GetGormDB()))
buf.Reset()
tx = s.db.GetGormDB().Begin()
los = &largeobject.LargeObjects{DB: tx}
s.Require().NoError(err)
s.Require().NoError(los.Get(blob.Oid, buf))
s.Equal(len(randomData), buf.Len())
s.Equal(randomData, buf.Bytes())
s.NoError(tx.Commit().Error)
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package schema

// TODO(ROX-17180): Remove this auto-generation at the beginning of 4.2 or at least
// before we made schema change to Blob store after first release.

//go:generate pg-schema-migration-helper --type=storage.Blob
1 change: 1 addition & 0 deletions migrator/runner/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,5 @@ import (
_ "github.com/stackrox/rox/migrator/migrations/m_177_to_m_178_group_permissions"
_ "github.com/stackrox/rox/migrator/migrations/m_178_to_m_179_embedded_collections_search_label"
_ "github.com/stackrox/rox/migrator/migrations/m_179_to_m_180_openshift_policy_exclusions"
_ "github.com/stackrox/rox/migrator/migrations/m_180_to_m_181_move_to_blobstore"
)
4 changes: 2 additions & 2 deletions pkg/migrations/internal/seq_num.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ var (
// CurrentDBVersionSeqNum is the current DB version number.
// This must be incremented every time we write a migration.
// It is a shared constant between central and the migrator binary.
CurrentDBVersionSeqNum = 180
CurrentDBVersionSeqNum = 181

// MinimumSupportedDBVersionSeqNum is the minimum DB version number
// that is supported by this database. This is used in case of rollbacks in
// the event that a major change introduced an incompatible schema update we
// can inform that a rollback below this is not supported by the database
MinimumSupportedDBVersionSeqNum = 180
MinimumSupportedDBVersionSeqNum = 181

// LastRocksDBVersionSeqNum is the sequence number for the last RocksDB version.
LastRocksDBVersionSeqNum = 112
Expand Down
Loading