Update model definitions and saving

This commit is contained in:
Chris Roberts 2022-09-28 08:38:34 -07:00
parent 4f559c06ab
commit 6970336d87
7 changed files with 120 additions and 139 deletions

View File

@ -21,18 +21,18 @@ type scope interface {
}
type Basis struct {
gorm.Model
Model
Vagrantfile *Vagrantfile `mapstructure:"-"`
VagrantfileID uint `mapstructure:"-"`
Vagrantfile *Vagrantfile `mapstructure:"Configuration" gorm:"OnDelete:Cascade"`
VagrantfileID *uint `mapstructure:"-"`
DataSource *ProtoValue
Jobs []*InternalJob `gorm:"polymorphic:Scope;" mapstructure:"-"`
Jobs []*InternalJob `gorm:"polymorphic:Scope" mapstructure:"-"`
Metadata MetadataSet
Name *string `gorm:"uniqueIndex,not null"`
Path *string `gorm:"uniqueIndex,not null"`
Projects []*Project
RemoteEnabled bool
ResourceId *string `gorm:"<-:create;uniqueIndex;not null"`
ResourceId *string `gorm:"<-:create,uniqueIndex,not null"`
}
func (b *Basis) scope() interface{} {
@ -65,7 +65,7 @@ func (b *Basis) Validate(tx *gorm.DB) error {
checkUnique(
tx.Model(&Basis{}).
Where(&Basis{Name: b.Name}).
Not(&Basis{Model: gorm.Model{ID: b.ID}}),
Not(&Basis{Model: Model{ID: b.ID}}),
),
),
),
@ -75,7 +75,7 @@ func (b *Basis) Validate(tx *gorm.DB) error {
checkUnique(
tx.Model(&Basis{}).
Where(&Basis{Path: b.Path}).
Not(&Basis{Model: gorm.Model{ID: b.ID}}),
Not(&Basis{Model: Model{ID: b.ID}}),
),
),
),
@ -85,7 +85,7 @@ func (b *Basis) Validate(tx *gorm.DB) error {
checkUnique(
tx.Model(&Basis{}).
Where(&Basis{ResourceId: b.ResourceId}).
Not(&Basis{Model: gorm.Model{ID: b.ID}}),
Not(&Basis{Model: Model{ID: b.ID}}),
),
),
),
@ -285,17 +285,8 @@ func (s *State) BasisPut(
return nil, saveErrorToStatus("basis", err)
}
if b.Configuration != nil {
if basis.Vagrantfile != nil {
basis.Vagrantfile.UpdateFromProto(b.Configuration)
} else {
basis.Vagrantfile = s.VagrantfileFromProto(b.Configuration)
}
}
result := s.db.Save(basis)
if result.Error != nil {
return nil, saveErrorToStatus("basis", result.Error)
if err := s.upsertFull(basis); err != nil {
return nil, saveErrorToStatus("basis", err)
}
return basis.ToProto(), nil

View File

@ -262,9 +262,8 @@ func (s *State) BoxPut(b *vagrant_server.Box) error {
return saveErrorToStatus("box", err)
}
result := s.db.Save(box)
if result.Error != nil {
return saveErrorToStatus("box", result.Error)
if err := s.upsertFull(box); err != nil {
return saveErrorToStatus("box", err)
}
return nil
@ -304,6 +303,9 @@ func (s *State) BoxFind(
if _, err := version.NewVersion(b.Version); err == nil {
box, err := s.BoxFromProtoRefFuzzy(b)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, lookupErrorToStatus("box", err)
}
@ -360,5 +362,5 @@ func (s *State) BoxFind(
return match.ToProto(), nil
}
return nil, lookupErrorToStatus("box", gorm.ErrRecordNotFound)
return nil, nil // lookupErrorToStatus("box", gorm.ErrRecordNotFound)
}

View File

@ -57,7 +57,7 @@ type InternalJob struct {
AssignTime *time.Time
AckTime *time.Time
AssignedRunnerID uint `mapstructure:"-"`
AssignedRunnerID *uint `mapstructure:"-"`
AssignedRunner *Runner
CancelTime *time.Time
CompleteTime *time.Time
@ -71,7 +71,7 @@ type InternalJob struct {
QueueTime *time.Time
Result *ProtoValue
Scope scope `gorm:"-:all"`
ScopeID uint `mapstructure:"-"`
ScopeID *uint `mapstructure:"-"`
ScopeType string `mapstructure:"-"`
State JobState
TargetRunner *ProtoValue
@ -94,19 +94,19 @@ func (i *InternalJob) BeforeCreate(tx *gorm.DB) error {
// If the job has a scope assigned to it, persist it.
func (i *InternalJob) BeforeSave(tx *gorm.DB) (err error) {
if i.Scope == nil {
i.ScopeID = 0
i.ScopeID = nil
i.ScopeType = ""
return nil
}
switch v := i.Scope.(type) {
case *Basis:
i.ScopeID = v.ID
i.ScopeID = &v.ID
i.ScopeType = "basis"
case *Project:
i.ScopeID = v.ID
i.ScopeID = &v.ID
i.ScopeType = "project"
case *Target:
i.ScopeID = v.ID
i.ScopeID = &v.ID
i.ScopeType = "target"
default:
return fmt.Errorf("unknown scope type (%T)", i.Scope)
@ -117,14 +117,14 @@ func (i *InternalJob) BeforeSave(tx *gorm.DB) (err error) {
// If the job has a scope, load it.
func (i *InternalJob) AfterFind(tx *gorm.DB) (err error) {
if i.ScopeID == 0 {
if i.ScopeID == nil {
return nil
}
switch i.ScopeType {
case "basis":
var b Basis
result := tx.Preload(clause.Associations).
First(&b, &Basis{Model: Model{ID: i.ScopeID}})
First(&b, &Basis{Model: Model{ID: *i.ScopeID}})
if result.Error != nil {
return result.Error
}
@ -132,7 +132,7 @@ func (i *InternalJob) AfterFind(tx *gorm.DB) (err error) {
case "project":
var p Project
result := tx.Preload(clause.Associations).
First(&p, &Project{Model: Model{ID: i.ScopeID}})
First(&p, &Project{Model: Model{ID: *i.ScopeID}})
if result.Error != nil {
return result.Error
}
@ -140,7 +140,7 @@ func (i *InternalJob) AfterFind(tx *gorm.DB) (err error) {
case "target":
var t Target
result := tx.Preload(clause.Associations).
First(&t, &Target{Model: Model{ID: i.ScopeID}})
First(&t, &Target{Model: Model{ID: *i.ScopeID}})
if result.Error != nil {
return result.Error
}

View File

@ -15,20 +15,20 @@ func init() {
}
type Project struct {
gorm.Model
Model
Basis *Basis
BasisID *uint `gorm:"uniqueIndex:idx_bname;not null" mapstructure:"-"`
Vagrantfile *Vagrantfile `mapstructure:"-"`
BasisID uint `gorm:"uniqueIndex:idx_bname" mapstructure:"-"`
Vagrantfile *Vagrantfile `gorm:"OnDelete:Cascade" mapstructure:"Configuration"`
VagrantfileID *uint `mapstructure:"-"`
DataSource *ProtoValue
Jobs []*InternalJob `gorm:"polymorphic:Scope;"`
Jobs []*InternalJob `gorm:"polymorphic:Scope"`
Metadata MetadataSet
Name *string `gorm:"uniqueIndex:idx_bname;not null"`
Path *string `gorm:"uniqueIndex;not null"`
Name *string `gorm:"uniqueIndex:idx_bname,not null"`
Path *string `gorm:"uniqueIndex,not null"`
RemoteEnabled bool
ResourceId *string `gorm:"<-:create;uniqueIndex;not null"`
Targets []*Target
ResourceId *string `gorm:"<-:create,uniqueIndex,not null"`
Targets []*Target `gorm:"OnDelete:Cascade"`
}
func (p *Project) scope() interface{} {
@ -49,16 +49,39 @@ func (p *Project) BeforeSave(tx *gorm.DB) error {
return nil
}
func (p *Project) BeforeUpdate(tx *gorm.DB) error {
// If a Vagrantfile was already set for the project, just update it
if p.Vagrantfile != nil && p.Vagrantfile.ID == 0 && p.VagrantfileID != nil {
var v Vagrantfile
result := tx.First(&v, &Vagrantfile{Model: Model{ID: *p.VagrantfileID}})
if result.Error != nil {
return result.Error
}
id := v.ID
if err := decode(p, &v); err != nil {
return err
}
v.ID = id
p.Vagrantfile = &v
}
return nil
}
func (p *Project) Validate(tx *gorm.DB) error {
err := validation.ValidateStruct(p,
// validation.Field(&p.Basis, validation.Required),
validation.Field(&p.BasisID,
validation.Required.When(p.Basis == nil),
),
validation.Field(&p.Basis,
validation.Required.When(p.BasisID == 0),
),
validation.Field(&p.Name,
validation.Required,
validation.By(
checkUnique(
tx.Model(&Project{}).
Where(&Project{Name: p.Name, BasisID: p.BasisID}).
Not(&Project{Model: gorm.Model{ID: p.ID}}),
Not(&Project{Model: Model{ID: p.ID}}),
),
),
),
@ -68,7 +91,7 @@ func (p *Project) Validate(tx *gorm.DB) error {
checkUnique(
tx.Model(&Project{}).
Where(&Project{Path: p.Path, BasisID: p.BasisID}).
Not(&Project{Model: gorm.Model{ID: p.ID}}),
Not(&Project{Model: Model{ID: p.ID}}),
),
),
),
@ -78,7 +101,7 @@ func (p *Project) Validate(tx *gorm.DB) error {
checkUnique(
tx.Model(&Project{}).
Where(&Project{ResourceId: p.ResourceId}).
Not(&Project{Model: gorm.Model{ID: p.ID}}),
Not(&Project{Model: Model{ID: p.ID}}),
),
),
),
@ -270,19 +293,8 @@ func (s *State) ProjectPut(
return nil, saveErrorToStatus("project", err)
}
// If a configuration came over the wire, either create one to attach
// to the project or update the existing one
if p.Configuration != nil {
if project.Vagrantfile != nil {
project.Vagrantfile.UpdateFromProto(p.Configuration)
} else {
project.Vagrantfile = s.VagrantfileFromProto(p.Configuration)
}
}
result := s.db.Save(project)
if result.Error != nil {
return nil, saveErrorToStatus("project", result.Error)
if err := s.upsertFull(project); err != nil {
return nil, saveErrorToStatus("project", err)
}
return project.ToProto(), nil

View File

@ -73,9 +73,8 @@ func (s *State) RunnerCreate(r *vagrant_server.Runner) error {
return saveErrorToStatus("runner", err)
}
result := s.db.Save(runner)
if result.Error != nil {
return saveErrorToStatus("runner", result.Error)
if err := s.upsertFull(runner); err != nil {
return saveErrorToStatus("runner", err)
}
return nil

View File

@ -5,7 +5,6 @@ import (
"fmt"
"github.com/go-ozzo/ozzo-validation/v4"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/vagrant-plugin-sdk/proto/vagrant_plugin_sdk"
"github.com/hashicorp/vagrant/internal/server"
"github.com/hashicorp/vagrant/internal/server/proto/vagrant_server"
@ -17,23 +16,22 @@ func init() {
}
type Target struct {
gorm.Model
Model
Configuration *ProtoRaw
Configuration *ProtoValue
Jobs []*InternalJob `gorm:"polymorphic:Scope;" mapstructure:"-"`
Metadata MetadataSet
Name *string `gorm:"uniqueIndex:idx_pname;not null"`
Parent *Target `gorm:"foreignkey:ID"`
ParentID uint `mapstructure:"-"`
ParentID *uint `mapstructure:"-"`
Project *Project
ProjectID *uint `gorm:"uniqueIndex:idx_pname;not null" mapstructure:"-"`
ProjectID uint `gorm:"uniqueIndex:idx_pname" mapstructure:"-"`
Provider *string
Record *ProtoRaw
Record *ProtoValue
ResourceId *string `gorm:"<-:create;uniqueIndex;not null"`
State vagrant_server.Operation_PhysicalState
Subtargets []*Target `gorm:"foreignkey:ParentID"`
Uuid *string `gorm:"uniqueIndex"`
l hclog.Logger
}
func (t *Target) scope() interface{} {
@ -63,7 +61,7 @@ func (t *Target) validate(tx *gorm.DB) error {
checkUnique(
tx.Model(&Target{}).
Where(&Target{Name: t.Name, ProjectID: t.ProjectID}).
Not(&Target{Model: gorm.Model{ID: t.ID}}),
Not(&Target{Model: Model{ID: t.ID}}),
),
),
),
@ -73,7 +71,7 @@ func (t *Target) validate(tx *gorm.DB) error {
checkUnique(
tx.Model(&Target{}).
Where(&Target{ResourceId: t.ResourceId}).
Not(&Target{Model: gorm.Model{ID: t.ID}}),
Not(&Target{Model: Model{ID: t.ID}}),
),
),
),
@ -83,12 +81,17 @@ func (t *Target) validate(tx *gorm.DB) error {
checkUnique(
tx.Model(&Target{}).
Where(&Target{Uuid: t.Uuid}).
Not(&Target{Model: gorm.Model{ID: t.ID}}),
Not(&Target{Model: Model{ID: t.ID}}),
),
),
),
),
// validation.Field(&t.ProjectID, validation.Required), TODO(spox): why are these empty?
validation.Field(&t.ProjectID,
validation.Required.When(t.Project == nil),
),
validation.Field(&t.Project,
validation.Required.When(t.ProjectID == 0),
),
)
if err != nil {
@ -221,30 +224,33 @@ func (s *State) TargetFromProtoFuzzy(
return nil, err
}
if t.Project == nil {
if t.Uuid == "" && t.Name == "" {
return nil, gorm.ErrRecordNotFound
}
if t.Project == nil && t.Uuid == "" {
return nil, ErrMissingProtoParent
}
target = &Target{}
query := &Target{Name: &t.Name}
tx := s.db.
Preload("Project",
s.db.Where(
&Project{ResourceId: &t.Project.ResourceId},
),
)
if t.Name != "" {
query.Name = &t.Name
}
if t.Uuid != "" {
query.Uuid = &t.Uuid
tx = tx.Or("uuid LIKE ?", fmt.Sprintf("%%%s%%", t.Uuid))
if t.Project != nil {
tx := s.search().
Joins("Project").
Preload("Project.Basis").
Where("Project.resource_id = ?", t.Project.ResourceId)
result := tx.First(target, &Target{Name: &t.Name})
if result.Error != nil {
return nil, result.Error
}
return target, nil
}
result := s.search().Joins("Project").
Preload("Project.Basis").
Where("Project.resource_id = ?", t.Project.ResourceId).
First(target, query)
tx := s.search().Preload("Project.Basis").
Where("uuid LIKE ?", fmt.Sprintf("%%%s%%", t.Uuid))
result := tx.First(target)
if result.Error != nil {
return nil, result.Error
}
@ -305,7 +311,7 @@ func (s *State) TargetDelete(
func (s *State) TargetPut(
t *vagrant_server.Target,
) (*vagrant_server.Target, error) {
target, err := s.TargetFromProto(t)
target, err := s.TargetFromProtoFuzzy(t)
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return nil, lookupErrorToStatus("target", err)
}
@ -315,23 +321,17 @@ func (s *State) TargetPut(
target = &Target{}
}
s.log.Info("pre-decode our target project", "project", target.Project)
err = s.softDecode(t, target)
if err != nil {
return nil, saveErrorToStatus("target", err)
}
s.log.Info("post-decode our target project", "project", target.Project)
if target.Project == nil {
panic("stop")
return nil, saveErrorToStatus("target", ErrMissingProtoParent)
}
result := s.db.Save(target)
s.log.Info("after save target project status", "project", target.Project, "error", err)
if result.Error != nil {
return nil, saveErrorToStatus("target", result.Error)
if err := s.upsertFull(target); err != nil {
return nil, saveErrorToStatus("target", err)
}
return target.ToProto(), nil

View File

@ -1,9 +1,7 @@
package state
import (
"github.com/hashicorp/vagrant-plugin-sdk/proto/vagrant_plugin_sdk"
"github.com/hashicorp/vagrant/internal/server/proto/vagrant_server"
"gorm.io/gorm"
)
type VagrantfileFormat uint8
@ -15,13 +13,13 @@ const (
)
type Vagrantfile struct {
gorm.Model
Model
Format VagrantfileFormat
Unfinalized *ProtoValue
Finalized *ProtoValue
Raw []byte
Path string
Path *string
}
func init() {
@ -33,54 +31,33 @@ func (v *Vagrantfile) ToProto() *vagrant_server.Vagrantfile {
return nil
}
vf := &vagrant_server.Vagrantfile{
Format: vagrant_server.Vagrantfile_Format(v.Format),
Raw: v.Raw,
}
if len(v.Path) > 0 {
vf.Path = &vagrant_plugin_sdk.Args_Path{
Path: v.Path,
}
}
if v.Unfinalized != nil {
vf.Unfinalized = v.Unfinalized.Message.(*vagrant_plugin_sdk.Args_Hash)
}
if v.Finalized != nil {
vf.Finalized = v.Finalized.Message.(*vagrant_plugin_sdk.Args_Hash)
var file vagrant_server.Vagrantfile
if err := decode(v, &file); err != nil {
panic("failed to decode vagrantfile: " + err.Error())
}
return vf
return &file
}
func (v *Vagrantfile) UpdateFromProto(vf *vagrant_server.Vagrantfile) *Vagrantfile {
v.Format = VagrantfileFormat(vf.Format)
v.Unfinalized = &ProtoValue{Message: vf.Unfinalized}
v.Finalized = &ProtoValue{Message: vf.Finalized}
v.Raw = vf.Raw
if vf.Unfinalized != nil {
v.Unfinalized = &ProtoValue{Message: vf.Unfinalized}
}
if vf.Finalized != nil {
v.Finalized = &ProtoValue{Message: vf.Finalized}
}
if vf.Path != nil {
v.Path = vf.Path.Path
v.Path = &vf.Path.Path
}
return v
}
func (s *State) VagrantfileFromProto(v *vagrant_server.Vagrantfile) *Vagrantfile {
file := &Vagrantfile{
Format: VagrantfileFormat(v.Format),
Raw: v.Raw,
}
if v.Unfinalized != nil {
file.Unfinalized = &ProtoValue{Message: v.Unfinalized}
}
if v.Finalized != nil {
file.Finalized = &ProtoValue{Message: v.Finalized}
}
if v.Path != nil {
file.Path = v.Path.Path
var file Vagrantfile
err := s.decode(v, &file)
if err != nil {
panic("failed to decode vagrantfile: " + err.Error())
}
return file
return &file
}