Skip to content
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

🐛 Try to make mquery checksums stable #952

Merged
merged 1 commit into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
70 changes: 68 additions & 2 deletions policy/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,58 @@ func detectVariantCyclesDFS(mrn string, statusMap map[string]nodeVisitStatus, qu
return nil
}

func topologicalSortQueries(queries []*explorer.Mquery) ([]*explorer.Mquery, error) {
// Gather all top-level queries with variants
queriesMap := map[string]*explorer.Mquery{}
for _, q := range queries {
if q == nil {
continue
}
if q.Mrn == "" {
// This should never happen. This function is called after all
// queries have their MRNs set.
panic("BUG: expected query MRN to be set for topological sort")
}
queriesMap[q.Mrn] = q
}

// Topologically sort the queries
sorted := &explorer.Mqueries{}
visited := map[string]struct{}{}
for _, q := range queriesMap {
err := topologicalSortQueriesDFS(q.Mrn, queriesMap, visited, sorted)
if err != nil {
return nil, err
}
}

return sorted.Items, nil
}

func topologicalSortQueriesDFS(queryMrn string, queriesMap map[string]*explorer.Mquery, visited map[string]struct{}, sorted *explorer.Mqueries) error {
if _, ok := visited[queryMrn]; ok {
return nil
}
visited[queryMrn] = struct{}{}
q := queriesMap[queryMrn]
if q == nil {
return nil
}
for _, variant := range q.Variants {
if variant.Mrn == "" {
// This should never happen. This function is called after all
// queries have their MRNs set.
panic("BUG: expected variant MRN to be set for topological sort")
}
err := topologicalSortQueriesDFS(variant.Mrn, queriesMap, visited, sorted)
if err != nil {
return err
}
}
sorted.Items = append(sorted.Items, q)
return nil
}

// Compile a bundle. See CompileExt for a full description.
func (p *Bundle) Compile(ctx context.Context, schema llx.Schema, library Library) (*PolicyBundleMap, error) {
return p.CompileExt(ctx, BundleCompileConf{
Expand Down Expand Up @@ -931,6 +983,13 @@ func (c *bundleCache) compileQueries(queries []*explorer.Mquery, policy *Policy)
return err
}

// Topologically sort the queries so that variant queries are compiled after the
// actual query they include.
topoSortedQueries, err := topologicalSortQueries(mergedQueries)
if err != nil {
return err
}

// After the first pass we may have errors. We try to collect as many errors
// as we can before returning, so more problems can be fixed at once.
// We have to return at this point, because these errors will prevent us from
Expand All @@ -939,11 +998,14 @@ func (c *bundleCache) compileQueries(queries []*explorer.Mquery, policy *Policy)
return c.error()
}

// Compile queries
for _, m := range topoSortedQueries {
c.compileQuery(m)
}

for i := range mergedQueries {
query := mergedQueries[i]
if query != nil {
c.compileQuery(query)

if query != queries[i] {
queries[i].Checksum = query.Checksum
queries[i].CodeId = query.CodeId
Expand All @@ -966,6 +1028,10 @@ func (c *bundleCache) precompileQuery(query *explorer.Mquery, policy *Policy) *e
return nil
}

if query.Title == "" {
preslavgerchev marked this conversation as resolved.
Show resolved Hide resolved
query.Title = query.Mql
}

// remove leading and trailing whitespace of docs, refs and tags
query.Sanitize()

Expand Down
17 changes: 17 additions & 0 deletions policy/bundle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,23 @@ func TestBundleCompile_FromQueryPackBundle(t *testing.T) {
require.Equal(t, 2, len(converted.Policies[0].Groups))
}

func TestStableMqueryChecksum(t *testing.T) {
bundle, err := policy.BundleFromPaths("../examples/complex.mql.yaml")
require.NoError(t, err)
require.NotNil(t, bundle)

bundlemap, err := bundle.Compile(context.Background(), schema, nil)
require.NoError(t, err)
require.NotNil(t, bundlemap)

for _, m := range bundlemap.Queries {
initialChecksum := m.Checksum
err := m.RefreshChecksum(context.Background(), schema, explorer.QueryMap(bundlemap.Queries).GetQuery)
require.NoError(t, err)
assert.Equal(t, initialChecksum, m.Checksum, "checksum for %s changed", m.Mrn)
}
}

func TestBundleCompile_RemoveFailingQueries(t *testing.T) {
bundleStr := `
owner_mrn: //test.sth
Expand Down