Skip to content

Commit

Permalink
⚠️ Use the new seeding library to seed rulesets (#450)
Browse files Browse the repository at this point in the history
* Migration v7 sets up the new unique UUID columns and deletes the old
RuleSets
* For the time being, the seeds are cloned into the Hub image from the
tackle2-seed repository.
* Seeds only run if the version number in the seed document matches the
DB migration version
* Seeds only run if the seed digest differs from that stored in the
`.hub.db.seed` setting.

Seeding operates via the following logic:

* If a model with the same UUID as a seed resource exists, check if it
is being renamed. If it is being renamed, ensure that the target name is
free by renaming any model that already has that name. Then proceed to
update the original model.
* If a model with the same UUID does not exist, look to see if one with
the same name already exists.
    * If one does not already exist, create a new model. 
* If one already exists and it was not created by the Hub (`CreateUser
!= ""`), rename it to free up the name and then create a new model.
* If one already exists and it was created by the Hub (`CreateUser ==
""`), commandeer it.

---------

Signed-off-by: Sam Lucidi <slucidi@redhat.com>
  • Loading branch information
mansam committed Jul 21, 2023
1 parent b4c6e3f commit 4686a21
Show file tree
Hide file tree
Showing 23 changed files with 1,053 additions and 1 deletion.
3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ FROM registry.access.redhat.com/ubi9/go-toolset:latest as builder
ENV GOPATH=$APP_ROOT
COPY --chown=1001:0 . .
RUN make docker
RUN git clone https://github.com/konveyor/tackle2-seed

FROM registry.access.redhat.com/ubi9/ubi-minimal
COPY --from=builder /opt/app-root/src/bin/hub /usr/local/bin/tackle-hub
COPY --from=builder /opt/app-root/src/auth/roles.yaml /tmp/roles.yaml
COPY --from=builder /opt/app-root/src/auth/users.yaml /tmp/users.yaml
COPY --from=builder /opt/app-root/src/tackle2-seed/resources/ /tmp/seed

RUN microdnf -y install \
sqlite \
&& microdnf -y clean all
Expand Down
5 changes: 5 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/konveyor/tackle2-hub/metrics"
"github.com/konveyor/tackle2-hub/migration"
"github.com/konveyor/tackle2-hub/reaper"
"github.com/konveyor/tackle2-hub/seeding"
"github.com/konveyor/tackle2-hub/settings"
"github.com/konveyor/tackle2-hub/task"
"github.com/konveyor/tackle2-hub/tracker"
Expand All @@ -41,6 +42,10 @@ func Setup() (db *gorm.DB, err error) {
if err != nil {
return
}
err = seeding.Seed()
if err != nil {
return
}
db, err = database.Open(true)
if err != nil {
return
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ require (
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/google/uuid v1.3.0
github.com/jortel/go-utils v0.1.1
github.com/konveyor/tackle2-seed v0.0.0-20230714183056-d397517afd22
github.com/mattn/go-sqlite3 v1.14.17
github.com/onsi/gomega v1.27.6
github.com/prometheus/client_golang v1.15.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/konveyor/tackle2-seed v0.0.0-20230714183056-d397517afd22 h1:6W0xmtOp/Uh9vs8ItNdzyQzNaf5ZTS7UBjbLsHYd4F4=
github.com/konveyor/tackle2-seed v0.0.0-20230714183056-d397517afd22/go.mod h1:9SLWPe6DTNhZ41PaNcPYoY9/ToYdMvwZtj/XFr2L+tU=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
Expand Down
2 changes: 2 additions & 0 deletions migration/pkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
v4 "github.com/konveyor/tackle2-hub/migration/v4"
v5 "github.com/konveyor/tackle2-hub/migration/v5"
v6 "github.com/konveyor/tackle2-hub/migration/v6"
v7 "github.com/konveyor/tackle2-hub/migration/v7"
"github.com/konveyor/tackle2-hub/settings"
"gorm.io/gorm"
)
Expand Down Expand Up @@ -45,5 +46,6 @@ func All() []Migration {
v4.Migration{},
v5.Migration{},
v6.Migration{},
v7.Migration{},
}
}
60 changes: 60 additions & 0 deletions migration/v7/migrate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package v7

import (
liberr "github.com/jortel/go-utils/error"
"github.com/jortel/go-utils/logr"
"github.com/konveyor/tackle2-hub/migration/v6/model"
"gorm.io/gorm"
)

var log = logr.WithName("migration|v7")

type Migration struct{}

func (r Migration) Apply(db *gorm.DB) (err error) {
// note: sqlite can't add a unique column, so we add UUID as a optional column,
// and then mark the column unique and create the index via auto-migrate.
type TagCategory struct {
model.TagCategory
UUID string
}

type Tag struct {
model.Tag
UUID string
}

type JobFunction struct {
model.JobFunction
UUID string
}

type RuleSet struct {
model.RuleSet
UUID string
}

err = db.AutoMigrate(&TagCategory{}, &Tag{}, &JobFunction{}, &RuleSet{})
if err != nil {
err = liberr.Wrap(err)
return
}

err = db.AutoMigrate(r.Models()...)
if err != nil {
err = liberr.Wrap(err)
return
}

err = db.Delete(&model.RuleSet{}, "CreateUser = ?", "").Error
if err != nil {
err = liberr.Wrap(err)
return
}

return
}

func (r Migration) Models() []interface{} {
return model.All()
}
25 changes: 25 additions & 0 deletions migration/v7/model/application.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package model

type Application struct {
Model
BucketOwner
Name string `gorm:"index;unique;not null"`
Description string
Review *Review `gorm:"constraint:OnDelete:CASCADE"`
Repository JSON `gorm:"type:json"`
Binary string
Facts []Fact `gorm:"constraint:OnDelete:CASCADE"`
Comments string
Tasks []Task `gorm:"constraint:OnDelete:CASCADE"`
Tags []Tag `gorm:"many2many:ApplicationTags"`
Identities []Identity `gorm:"many2many:ApplicationIdentity;constraint:OnDelete:CASCADE"`
BusinessServiceID *uint `gorm:"index"`
BusinessService *BusinessService
OwnerID *uint `gorm:"index"`
Owner *Stakeholder `gorm:"foreignKey:OwnerID"`
Contributors []Stakeholder `gorm:"many2many:ApplicationContributors;constraint:OnDelete:CASCADE"`
Analyses []Analysis `gorm:"constraint:OnDelete:CASCADE"`
MigrationWaveID *uint `gorm:"index"`
MigrationWave *MigrationWave
Ticket *Ticket `gorm:"constraint:OnDelete:CASCADE"`
}
19 changes: 19 additions & 0 deletions migration/v7/model/applicationtag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package model

//
// ApplicationTag represents a row in the join table for the
// many-to-many relationship between Applications and Tags.
type ApplicationTag struct {
ApplicationID uint `gorm:"primaryKey"`
TagID uint `gorm:"primaryKey"`
Source string `gorm:"primaryKey;not null"`
Application Application `gorm:"constraint:OnDelete:CASCADE"`
Tag Tag `gorm:"constraint:OnDelete:CASCADE"`
}

//
// TableName must return "ApplicationTags" to ensure compatibility
// with the autogenerated join table name.
func (ApplicationTag) TableName() string {
return "ApplicationTags"
}
10 changes: 10 additions & 0 deletions migration/v7/model/businessservice.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package model

type BusinessService struct {
Model
Name string `gorm:"index;unique;not null"`
Description string
Applications []Application `gorm:"constraint:OnDelete:SET NULL"`
StakeholderID *uint `gorm:"index"`
Stakeholder *Stakeholder
}
9 changes: 9 additions & 0 deletions migration/v7/model/jobfunction.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package model

type JobFunction struct {
Model
UUID string `gorm:"uniqueIndex"`
Username string
Name string `gorm:"index;unique;not null"`
Stakeholders []Stakeholder `gorm:"constraint:OnDelete:SET NULL"`
}
13 changes: 13 additions & 0 deletions migration/v7/model/migrationwave.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package model

import "time"

type MigrationWave struct {
Model
Name string `gorm:"uniqueIndex:MigrationWaveA"`
StartDate time.Time `gorm:"uniqueIndex:MigrationWaveA"`
EndDate time.Time `gorm:"uniqueIndex:MigrationWaveA"`
Applications []Application `gorm:"constraint:OnDelete:SET NULL"`
Stakeholders []Stakeholder `gorm:"many2many:MigrationWaveStakeholders;constraint:OnDelete:CASCADE"`
StakeholderGroups []StakeholderGroup `gorm:"many2many:MigrationWaveStakeholderGroups;constraint:OnDelete:CASCADE"`
}
72 changes: 72 additions & 0 deletions migration/v7/model/pkg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package model

import "github.com/konveyor/tackle2-hub/migration/v6/model"

//
// JSON field (data) type.
type JSON = []byte

type Model = model.Model
type TechDependency = model.TechDependency
type Incident = model.Incident
type Analysis = model.Analysis
type Issue = model.Issue
type Bucket = model.Bucket
type BucketOwner = model.BucketOwner
type Dependency = model.Dependency
type File = model.File
type Fact = model.Fact
type Identity = model.Identity
type Import = model.Import
type ImportSummary = model.ImportSummary
type ImportTag = model.ImportTag
type Proxy = model.Proxy
type Review = model.Review
type Setting = model.Setting
type Task = model.Task
type TaskGroup = model.TaskGroup
type TaskReport = model.TaskReport
type Ticket = model.Ticket
type Tracker = model.Tracker
type TTL = model.TTL
type DependencyCyclicError = model.DependencyCyclicError

//
// All builds all models.
// Models are enumerated such that each are listed after
// all the other models on which they may depend.
func All() []interface{} {
return []interface{}{
Application{},
TechDependency{},
Incident{},
Analysis{},
Issue{},
Bucket{},
BusinessService{},
Dependency{},
File{},
Fact{},
Identity{},
Import{},
ImportSummary{},
ImportTag{},
JobFunction{},
MigrationWave{},
Proxy{},
Review{},
Setting{},
RuleSet{},
Rule{},
Stakeholder{},
StakeholderGroup{},
Tag{},
TagCategory{},
Task{},
TaskGroup{},
TaskReport{},
Ticket{},
Tracker{},
ApplicationTag{},
}
}
68 changes: 68 additions & 0 deletions migration/v7/model/ruleset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package model

import "gorm.io/gorm"

//
// RuleSet - Analysis ruleset.
type RuleSet struct {
Model
UUID string `gorm:"uniqueIndex"`
Kind string
Name string `gorm:"uniqueIndex;not null"`
Description string
Custom bool
Repository JSON `gorm:"type:json"`
ImageID uint `gorm:"index" ref:"file"`
Image *File
IdentityID *uint `gorm:"index"`
Identity *Identity
Rules []Rule `gorm:"constraint:OnDelete:CASCADE"`
DependsOn []RuleSet `gorm:"many2many:RuleSetDependencies;constraint:OnDelete:CASCADE"`
}

//
// BeforeUpdate hook to avoid cyclic dependencies.
func (r *RuleSet) BeforeUpdate(db *gorm.DB) (err error) {
seen := make(map[uint]bool)
var nextDeps []RuleSet
var nextRuleSetIDs []uint
for _, dep := range r.DependsOn {
nextRuleSetIDs = append(nextRuleSetIDs, dep.ID)
}
for len(nextRuleSetIDs) != 0 {
result := db.Preload("DependsOn").Where("ID IN ?", nextRuleSetIDs).Find(&nextDeps)
if result.Error != nil {
err = result.Error
return
}
nextRuleSetIDs = nextRuleSetIDs[:0]
for _, nextDep := range nextDeps {
for _, dep := range nextDep.DependsOn {
if seen[dep.ID] {
continue
}
if dep.ID == r.ID {
err = DependencyCyclicError{}
return
}
seen[dep.ID] = true
nextRuleSetIDs = append(nextRuleSetIDs, dep.ID)
}
}
}

return
}

//
// Rule - Analysis rule.
type Rule struct {
Model
Name string
Description string
Labels JSON `gorm:"type:json"`
RuleSetID uint `gorm:"uniqueIndex:RuleA;not null"`
RuleSet *RuleSet
FileID *uint `gorm:"uniqueIndex:RuleA" ref:"file"`
File *File
}
14 changes: 14 additions & 0 deletions migration/v7/model/stakeholder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package model

type Stakeholder struct {
Model
Name string `gorm:"not null;"`
Email string `gorm:"index;unique;not null"`
Groups []StakeholderGroup `gorm:"many2many:StakeholderGroupStakeholder;constraint:OnDelete:CASCADE"`
BusinessServices []BusinessService `gorm:"constraint:OnDelete:SET NULL"`
JobFunctionID *uint `gorm:"index"`
JobFunction *JobFunction
Owns []Application `gorm:"foreignKey:OwnerID;constraint:OnDelete:SET NULL"`
Contributes []Application `gorm:"many2many:ApplicationContributors;constraint:OnDelete:CASCADE"`
MigrationWaves []MigrationWave `gorm:"many2many:MigrationWaveStakeholders;constraint:OnDelete:CASCADE"`
}
10 changes: 10 additions & 0 deletions migration/v7/model/stakeholdergroup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package model

type StakeholderGroup struct {
Model
Name string `gorm:"index;unique;not null"`
Username string
Description string
Stakeholders []Stakeholder `gorm:"many2many:StakeholderGroupStakeholder;constraint:OnDelete:CASCADE"`
MigrationWaves []MigrationWave `gorm:"many2many:MigrationWaveStakeholderGroups;constraint:OnDelete:CASCADE"`
}
10 changes: 10 additions & 0 deletions migration/v7/model/tag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package model

type Tag struct {
Model
UUID string `gorm:"uniqueIndex"`
Name string `gorm:"uniqueIndex:tagA;not null"`
Username string
CategoryID uint `gorm:"uniqueIndex:tagA;index;not null"`
Category TagCategory
}
11 changes: 11 additions & 0 deletions migration/v7/model/tagcategory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package model

type TagCategory struct {
Model
UUID string `gorm:"uniqueIndex"`
Name string `gorm:"index;unique;not null"`
Username string
Rank uint
Color string
Tags []Tag `gorm:"foreignKey:CategoryID;constraint:OnDelete:CASCADE"`
}
Loading

0 comments on commit 4686a21

Please sign in to comment.