-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: migration dependencies (#1197)
* feat: migration dependency graph * feat: run dependent scripts * test: migration dependency * fix: don't read dir. use the embed.FS when parsing dependencies. * feat: add more dependency headers
- Loading branch information
1 parent
ad99acd
commit 27c2266
Showing
6 changed files
with
304 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package migrate | ||
|
||
import ( | ||
"bufio" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/flanksource/duty/functions" | ||
"github.com/flanksource/duty/views" | ||
"github.com/samber/lo" | ||
) | ||
|
||
func parseDependencies(script string) ([]string, error) { | ||
const dependencyHeader = "-- dependsOn: " | ||
|
||
var dependencies []string | ||
scanner := bufio.NewScanner(strings.NewReader(script)) | ||
for scanner.Scan() { | ||
line := scanner.Text() | ||
if !strings.HasPrefix(line, dependencyHeader) { | ||
break | ||
} | ||
|
||
line = strings.TrimPrefix(line, dependencyHeader) | ||
deps := strings.Split(line, ",") | ||
dependencies = append(dependencies, lo.Map(deps, func(x string, _ int) string { | ||
return strings.TrimSpace(x) | ||
})...) | ||
} | ||
|
||
return dependencies, nil | ||
} | ||
|
||
// DependencyMap map holds path -> dependents | ||
type DependencyMap map[string][]string | ||
|
||
// getDependencyTree returns a list of scripts and its dependents | ||
// | ||
// example: if a.sql dependsOn b.sql, c.sql | ||
// it returns | ||
// | ||
// { | ||
// b.sql: []string{a.sql}, | ||
// c.sql: []string{a.sql}, | ||
// } | ||
func getDependencyTree() (DependencyMap, error) { | ||
graph := make(DependencyMap) | ||
|
||
funcs, err := functions.GetFunctions() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
views, err := views.GetViews() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
for i, dir := range []map[string]string{funcs, views} { | ||
dirName := "functions" | ||
if i == 1 { | ||
dirName = "views" | ||
} | ||
|
||
for entry, content := range dir { | ||
path := filepath.Join(dirName, entry) | ||
dependents, err := parseDependencies(content) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
for _, dependent := range dependents { | ||
graph[dependent] = append(graph[dependent], strings.TrimPrefix(path, "../")) | ||
} | ||
} | ||
} | ||
|
||
return graph, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package migrate | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/onsi/gomega" | ||
) | ||
|
||
func TestParseDependencies(t *testing.T) { | ||
testdata := []struct { | ||
script string | ||
want []string | ||
}{ | ||
{ | ||
script: "-- dependsOn: a.sql, b.sql", | ||
want: []string{"a.sql", "b.sql"}, | ||
}, | ||
{ | ||
script: "SELECT 1;", | ||
want: nil, | ||
}, | ||
{ | ||
script: "-- dependsOn: a.sql, b.sql,c.sql", | ||
want: []string{"a.sql", "b.sql", "c.sql"}, | ||
}, | ||
} | ||
|
||
g := gomega.NewWithT(t) // use gomega with std go tests | ||
for _, td := range testdata { | ||
got, err := parseDependencies(td.script) | ||
if err != nil { | ||
t.Fatal(err.Error()) | ||
} | ||
|
||
g.Expect(got).To(gomega.Equal(td.want)) | ||
} | ||
} | ||
|
||
func TestDependencyMap(t *testing.T) { | ||
g := gomega.NewWithT(t) // use gomega with std go tests | ||
|
||
graph, err := getDependencyTree() | ||
if err != nil { | ||
t.Fatal(err.Error()) | ||
} | ||
|
||
expected := map[string][]string{ | ||
"functions/drop.sql": {"views/006_config_views.sql", "views/021_notification.sql"}, | ||
"views/006_config_views.sql": {"views/021_notification.sql"}, | ||
} | ||
|
||
g.Expect(graph).To(gomega.HaveLen(len(expected))) | ||
|
||
for key, expectedDeps := range expected { | ||
g.Expect(graph).To(gomega.HaveKey(key)) | ||
g.Expect(graph[key]).To(gomega.ConsistOf(expectedDeps)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package tests | ||
|
||
import ( | ||
"github.com/flanksource/duty/migrate" | ||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
) | ||
|
||
var _ = Describe("migration dependency", Ordered, func() { | ||
It("should have no executable scripts", func() { | ||
db, err := DefaultContext.DB().DB() | ||
Expect(err).To(BeNil()) | ||
|
||
funcs, views, err := migrate.GetExecutableScripts(db) | ||
Expect(err).To(BeNil()) | ||
Expect(len(funcs)).To(BeZero()) | ||
Expect(len(views)).To(BeZero()) | ||
}) | ||
|
||
// FIXME: sql driver issue on CI | ||
// It("should get correct executable scripts", func() { | ||
// err := DefaultContext.DB().Exec(`UPDATE migration_logs SET hash = 'dummy' WHERE path = 'drop.sql'`).Error | ||
// Expect(err).To(BeNil()) | ||
// | ||
// db, err := DefaultContext.DB().DB() | ||
// Expect(err).To(BeNil()) | ||
// | ||
// funcs, views, err := migrate.GetExecutableScripts(db) | ||
// Expect(err).To(BeNil()) | ||
// Expect(len(funcs)).To(Equal(1)) | ||
// Expect(len(views)).To(Equal(2)) | ||
// | ||
// Expect(collections.MapKeys(funcs)).To(Equal([]string{"drop.sql"})) | ||
// Expect(collections.MapKeys(views)).To(ConsistOf([]string{"006_config_views.sql", "021_notification.sql"})) | ||
// | ||
// { | ||
// // run the migrations again to ensure that the hashes are repopulated | ||
// err := migrate.RunMigrations(db, api.DefaultConfig) | ||
// Expect(err).To(BeNil()) | ||
// | ||
// // at the end, there should be no scrips to apply | ||
// db, err := DefaultContext.DB().DB() | ||
// Expect(err).To(BeNil()) | ||
// | ||
// funcs, views, err := migrate.GetExecutableScripts(db) | ||
// Expect(err).To(BeNil()) | ||
// Expect(len(funcs)).To(BeZero()) | ||
// Expect(len(views)).To(BeZero()) | ||
// } | ||
// }) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters