Skip to content

Commit

Permalink
⭐ add run info (#2280)
Browse files Browse the repository at this point in the history
Add a new info functionality to the run-command. This collects stats about aquery you are interested in. For example: it collects information about the resources and fields that the query is going to execute:

```bash
> cnquery run -c "sshd.config.params[Version] == mondoo.version" --info
```

which returns:

```bash
Resources and Fields used:
- sshd.config
  - params
- mondoo
  - version
```

We can extend it to more information in the future.

Signed-off-by: Dominik Richter <[email protected]>
  • Loading branch information
arlimus authored Oct 18, 2023
1 parent 1930d68 commit e930e4a
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 28 deletions.
17 changes: 17 additions & 0 deletions apps/cnquery/cmd/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,23 @@ func (c *cnqueryPlugin) RunQuery(conf *run.RunQueryConfig, runtime *providers.Ru
return nil
}

if conf.DoInfo {
ast, err := parser.Parse(mqlc.Dedent(conf.Command))
if ast == nil {
return errors.Wrap(err, "failed to parse command")
}

conf := mqlc.NewConfig(runtime.Schema(), conf.Features)
conf.EnableStats()
_, err = mqlc.CompileAST(ast, nil, conf)
if err != nil {
return errors.Wrap(err, "failed to compile command")
}

out.WriteString(printer.DefaultPrinter.CompilerStats(conf.Stats))
return nil
}

var upstreamConfig *upstream.UpstreamConfig
serviceAccount := opts.GetServiceCredential()
if serviceAccount != nil {
Expand Down
2 changes: 2 additions & 0 deletions apps/cnquery/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func init() {
RunCmd.Flags().StringP("command", "c", "", "MQL query to executed in the shell.")
RunCmd.Flags().Bool("parse", false, "Parse the query and return the logical structure.")
RunCmd.Flags().Bool("ast", false, "Parse the query and return the abstract syntax tree (AST).")
RunCmd.Flags().Bool("info", false, "Parse the query and provide information about it.")
RunCmd.Flags().BoolP("json", "j", false, "Run the query and return the object in a JSON structure.")
RunCmd.Flags().String("platform-id", "", "Select a specific target asset by providing its platform ID.")

Expand All @@ -47,6 +48,7 @@ var RunCmdRun = func(cmd *cobra.Command, runtime *providers.Runtime, cliRes *plu

conf.Command, _ = cmd.Flags().GetString("command")
conf.DoAst, _ = cmd.Flags().GetBool("ast")
conf.DoInfo, _ = cmd.Flags().GetBool("info")
conf.DoParse, _ = cmd.Flags().GetBool("parse")
if doJSON, _ := cmd.Flags().GetBool("json"); doJSON {
conf.Format = "json"
Expand Down
15 changes: 14 additions & 1 deletion cli/printer/mql.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"time"

"go.mondoo.com/cnquery/v9/llx"
"go.mondoo.com/cnquery/v9/mqlc"
"go.mondoo.com/cnquery/v9/types"
"go.mondoo.com/cnquery/v9/utils/sortx"
"golang.org/x/exp/slices"
Expand Down Expand Up @@ -747,7 +748,19 @@ func (print *Printer) DataWithLabel(r *llx.RawData, codeID string, bundle *llx.C
return b.String()
}

// CodeBundle prints a bundle to a string
func (print *Printer) CompilerStats(stats *mqlc.CompilerStats) string {
var res strings.Builder
res.WriteString("Resources and Fields used:\n")
for resource, fields := range stats.ResourceFields {
res.WriteString("- " + print.Yellow(resource) + "\n")
for field := range fields {
res.WriteString(" - " + print.Primary(field) + "\n")
}
}
return res.String()
}

// CodeBundle prints the contents of the MQL query
func (print *Printer) CodeBundle(bundle *llx.CodeBundle) string {
var res strings.Builder

Expand Down
37 changes: 35 additions & 2 deletions mqlc/mqlc.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,28 @@ func (vm *varmap) len() int {
type compilerConfig struct {
Schema llx.Schema
UseAssetContext bool
Stats *CompilerStats
}

func (c *compilerConfig) EnableStats() {
c.Stats = &CompilerStats{
ResourceFields: map[string]map[string]struct{}{},
}
}

type CompilerStats struct {
ResourceFields map[string]map[string]struct{}
}

func (c *CompilerStats) calledResource(name string) {
if _, ok := c.ResourceFields[name]; !ok {
c.ResourceFields[name] = map[string]struct{}{}
}
}

func (c *CompilerStats) calledField(resource string, field string) {
c.calledResource(resource)
c.ResourceFields[resource][field] = struct{}{}
}

func NewConfig(schema llx.Schema, features cnquery.Features) compilerConfig {
Expand Down Expand Up @@ -956,12 +978,15 @@ func (c *compiler) compileBoundIdentifierWithMqlCtx(id string, binding *variable
fieldPath, fieldinfos, ok := c.findField(resource, id)
if ok {
fieldinfo := fieldinfos[len(fieldinfos)-1]
if c.compilerConfig.Stats != nil {
c.compilerConfig.Stats.calledField(resource.Name, fieldinfo.Name)
}

if call != nil && len(call.Function) > 0 && !fieldinfo.IsImplicitResource {
return true, types.Nil, errors.New("cannot call resource field with arguments yet")
}

c.Result.MinMondooVersion = getMinMondooVersion(c.Schema, c.Result.MinMondooVersion, typ.ResourceName(), id)
c.Result.MinMondooVersion = getMinMondooVersion(c.Schema, c.Result.MinMondooVersion, resource.Name, id)

// this only happens when we call a field of a bridging resource,
// in which case we don't call the field (since there is nothing to do)
Expand Down Expand Up @@ -1049,6 +1074,10 @@ func (c *compiler) compileBoundIdentifierWithoutMqlCtx(id string, binding *varia
}

if fieldinfo != nil {
if c.compilerConfig.Stats != nil {
c.compilerConfig.Stats.calledField(resource.Name, fieldinfo.Name)
}

if call != nil && len(call.Function) > 0 {
return true, types.Nil, errors.New("cannot call resource field with arguments yet")
}
Expand All @@ -1057,7 +1086,7 @@ func (c *compiler) compileBoundIdentifierWithoutMqlCtx(id string, binding *varia
return true, types.Nil, fmt.Errorf("field '%s' on '%s' requires the MQLAssetContext feature", id, typ.Label())
}

c.Result.MinMondooVersion = getMinMondooVersion(c.Schema, c.Result.MinMondooVersion, typ.ResourceName(), id)
c.Result.MinMondooVersion = getMinMondooVersion(c.Schema, c.Result.MinMondooVersion, resource.Name, id)

// this only happens when we call a field of a bridging resource,
// in which case we don't call the field (since there is nothing to do)
Expand Down Expand Up @@ -1117,6 +1146,10 @@ func (c *compiler) compileResource(id string, calls []*parser.Call) (bool, []*pa
calls = calls[1:]
}

if c.compilerConfig.Stats != nil {
c.compilerConfig.Stats.calledResource(resource.Name)
}

var call *parser.Call
if len(calls) > 0 && calls[0].Function != nil {
call = calls[0]
Expand Down
59 changes: 34 additions & 25 deletions shared/proto/cnquery.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions shared/proto/cnquery.proto
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ message RunQueryConfig {

bool do_parse = 5;
bool do_ast = 6;
bool do_info = 13;
bool do_record = 7;
string format = 8;
string platform_id = 9;
Expand Down

0 comments on commit e930e4a

Please sign in to comment.