New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: new standalone lineage table + associated migration #173
Changes from 2 commits
95f2155
d2e151d
a634e4a
a52b8ce
c4d72ef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ package db | |
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"net/url" | ||
"strconv" | ||
|
@@ -55,6 +56,7 @@ func Init(config config.DBConfig, debug bool) *Database { | |
&types.Resource{}, | ||
&types.Attribute{}, | ||
&types.OutputValue{}, | ||
&types.Lineage{}, | ||
&types.Plan{}, | ||
&types.PlanModel{}, | ||
&types.PlanModelVariable{}, | ||
|
@@ -76,20 +78,70 @@ func Init(config config.DBConfig, debug bool) *Database { | |
if debug { | ||
db.Config.Logger.LogMode(logger.Info) | ||
} | ||
return &Database{db} | ||
|
||
d := &Database{db} | ||
if err = d.MigrateLineage(); err != nil { | ||
log.Fatalf("Lineage migration failed: %v\n", err) | ||
} | ||
|
||
return d | ||
} | ||
|
||
// MigrateLineage is a migration function to update db and its data to the | ||
// new lineage db scheme. It will update State table data, delete "lineage" column | ||
// and add corresponding Lineage entries | ||
func (db *Database) MigrateLineage() error { | ||
if db.Migrator().HasColumn(&types.State{}, "lineage") { | ||
states := db.ListStates() | ||
for _, stPath := range states { | ||
// Recover State from db for update | ||
var st types.State | ||
res := db.First(&st, types.State{Path: stPath}) | ||
if errors.Is(res.Error, gorm.ErrRecordNotFound) { | ||
return fmt.Errorf("State not found in db") | ||
} | ||
|
||
if err := db.UpdateState(st); err != nil { | ||
return fmt.Errorf("Failed to update %s state during lineage migration: %v", stPath, err) | ||
} | ||
} | ||
|
||
// Custom migration rules | ||
if err := db.Migrator().DropColumn(&types.State{}, "lineage"); err != nil { | ||
return fmt.Errorf("Failed to drop lineage column during migration: %v", err) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
type attributeValues map[string]interface{} | ||
|
||
func (db *Database) stateS3toDB(sf *statefile.File, path string, versionID string) (st types.State) { | ||
var version types.Version | ||
db.First(&version, types.Version{VersionID: versionID}) | ||
st = types.State{ | ||
Path: path, | ||
Version: version, | ||
TFVersion: sf.TerraformVersion.String(), | ||
Serial: int64(sf.Serial), | ||
Lineage: sf.Lineage, | ||
|
||
// Check if the associated lineage is already present in lineages table | ||
// If so, it recovers its ID otherwise it inserts it at the same time as the state | ||
var lineage types.Lineage | ||
if errors.Is(db.First(&lineage, types.Lineage{Value: sf.Lineage}).Error, gorm.ErrRecordNotFound) { | ||
st = types.State{ | ||
Path: path, | ||
Version: version, | ||
TFVersion: sf.TerraformVersion.String(), | ||
Serial: int64(sf.Serial), | ||
Lineage: types.Lineage{ | ||
Value: sf.Lineage, | ||
}, | ||
} | ||
} else { | ||
st = types.State{ | ||
Path: path, | ||
Version: version, | ||
TFVersion: sf.TerraformVersion.String(), | ||
Serial: int64(sf.Serial), | ||
LineageID: lineage.ID, | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens if there's an error but it's not There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On the Gorm.v2 documentation, they only evoke this error for db.First, so I started from the principle that there could be no other ... An interpretation that may be wrong, however, what do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it'd be safer to have 3 cases:
|
||
} | ||
|
||
for _, m := range sf.State.Modules { | ||
|
@@ -169,6 +221,29 @@ func (db *Database) InsertState(path string, versionID string, sf *statefile.Fil | |
return nil | ||
} | ||
|
||
// UpdateState update a Terraform State in the Database with Lineage foreign constraint | ||
// It will also insert Lineage entry in the db if needed | ||
func (db *Database) UpdateState(st types.State) error { | ||
raphink marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Get lineage from old column | ||
if err := db.Raw("SELECT lineage FROM states WHERE path = ?", st.Path).Scan(&st.Lineage.Value).Error; err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't there a way do do that using the Go structs? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not to my knowledge, knowing that it is a request to recover an element which is no longer present in the structure during the migration. |
||
return fmt.Errorf("Error on %s lineage recovering during migration: %v", st.Path, err) | ||
} | ||
|
||
// Create Lineage entry if not exist (value column is unique) | ||
db.Create(&st.Lineage) | ||
|
||
// Get Lineage ID for foreign constraint | ||
var lineage types.Lineage | ||
res := db.First(&lineage, lineage) | ||
if errors.Is(res.Error, gorm.ErrRecordNotFound) { | ||
return fmt.Errorf("State's lineage not found in db during update") | ||
} | ||
st.LineageID = lineage.ID | ||
st.Lineage = lineage | ||
|
||
return db.Save(&st).Error | ||
} | ||
|
||
// InsertVersion inserts an AWS S3 Version in the Database | ||
func (db *Database) InsertVersion(version *state.Version) error { | ||
var v types.Version | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it's just me, but I find this line a bit loaded. Can we split into:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Arguably, this should never happen this the migration is done when Terraboard is launched. Or am I missing something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Precisely, this condition was introduced by the arrival of the new lineage table. Indeed, when we insert a state there are two possible scenarios: