Skip to content

Commit b14a4ac

Browse files
authored
✨ cnquery run --exit-1-on-failure (#5656)
This change adds a new flag named `--exit-1-on-failure` which will make `cnquery` exit with code `1` if any query result failed. Simple Example: ``` cnquery run -c "1==2" --exit-1-on-failure ``` Closes mondoohq/cnspec#1671 Signed-off-by: Salim Afiune Maya <afiune@mondoo.com>
1 parent 91dde6b commit b14a4ac

File tree

10 files changed

+151
-37
lines changed

10 files changed

+151
-37
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ dist
33
alpine-container.tar
44
centos-container.tar
55
./cnquery
6+
test/commands/cnquery
67
file1
78
id_rsa
89
id_rsa.pub

apps/cnquery/cmd/plugin.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,17 @@ func (c *cnqueryPlugin) RunQuery(conf *run.RunQueryConfig, runtime *providers.Ru
123123
out.WriteString("[")
124124
}
125125

126+
// anyResultFailed is a flag that will be switched on if any query result failed,
127+
// if the flag `exit-1-on-failure` is provided and anyResultFailed is true, we
128+
// will exit the program with the exit code `1`
129+
var anyResultFailed = false
130+
// we defer this check since we want it to be the last thing to be evaluated
131+
defer func() {
132+
if conf.GetExit_1OnFailure() && anyResultFailed {
133+
os.Exit(1)
134+
}
135+
}()
136+
126137
for i := range discoveredAssets.Assets {
127138
asset := discoveredAssets.Assets[i]
128139

@@ -184,6 +195,17 @@ func (c *cnqueryPlugin) RunQuery(conf *run.RunQueryConfig, runtime *providers.Ru
184195
return errors.Wrap(err, "failed to run")
185196
}
186197

198+
// check if any result failed
199+
for _, result := range results {
200+
if result == nil || result.Data == nil {
201+
continue
202+
}
203+
204+
if truthy, ok := result.Data.IsTruthy(); ok && !truthy {
205+
anyResultFailed = true
206+
}
207+
}
208+
187209
if conf.Format == "llx" && conf.Output != "" {
188210
out, err := proto.Marshal(code)
189211
if err != nil {
@@ -204,7 +226,6 @@ func (c *cnqueryPlugin) RunQuery(conf *run.RunQueryConfig, runtime *providers.Ru
204226
out.WriteString(",")
205227
}
206228
}
207-
208229
}
209230

210231
if conf.Format == "json" {

apps/cnquery/cmd/run.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ func init() {
3232
_ = RunCmd.Flags().MarkHidden("use-llx")
3333
RunCmd.Flags().StringToString("annotations", nil, "Specify annotations for this run")
3434
_ = RunCmd.Flags().MarkHidden("annotations")
35+
RunCmd.Flags().Bool("exit-1-on-failure", false, "Exit with error code 1 if one or more query results fail")
3536
}
3637

3738
var RunCmd = &cobra.Command{
@@ -53,6 +54,7 @@ var RunCmdRun = func(cmd *cobra.Command, runtime *providers.Runtime, cliRes *plu
5354
conf.DoAst, _ = cmd.Flags().GetBool("ast")
5455
conf.DoInfo, _ = cmd.Flags().GetBool("info")
5556
conf.DoParse, _ = cmd.Flags().GetBool("parse")
57+
conf.Exit_1OnFailure, _ = cmd.Flags().GetBool("exit-1-on-failure")
5658
if doJSON, _ := cmd.Flags().GetBool("json"); doJSON {
5759
conf.Format = "json"
5860
}

shared/proto/cnquery.pb.go

Lines changed: 27 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

shared/proto/cnquery.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ message RunQueryConfig {
2424
bool incognito = 10;
2525
string output = 11;
2626
string input = 12;
27+
bool exit_1_on_failure = 14;
2728
}
2829

2930
message Empty {

test/commands/cli_test.go

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,55 @@
44
package commands
55

66
import (
7+
"log"
78
"os"
9+
"os/exec"
10+
"path"
11+
"path/filepath"
812
"sync"
913
"testing"
1014

1115
cmdtest "github.com/google/go-cmdtest"
1216
"github.com/spf13/cobra"
1317
"github.com/stretchr/testify/require"
14-
"go.mondoo.com/cnquery/v11/apps/cnquery/cmd"
1518
)
1619

1720
var once sync.Once
21+
var testDir string
1822
var cnqueryCmd *cobra.Command
1923

2024
func setup() {
21-
var err error
22-
cnqueryCmd, err = cmd.BuildRootCmd()
25+
// build cnquery
26+
if err := exec.Command("go", "build", "../../apps/cnquery/cnquery.go").Run(); err != nil {
27+
log.Fatalf("building cnquery: %v", err)
28+
}
29+
30+
// install local provider
31+
if err := exec.Command("bash", "-c", "cd ../.. && make providers/build/os providers/install/os").Run(); err != nil {
32+
log.Fatalf("building os provider: %v", err)
33+
}
34+
35+
// create a fake directory to use for testing purposes (providers, config, etc.)
36+
dir, err := os.MkdirTemp("", "mondoo")
37+
if err != nil {
38+
log.Fatalf("creating directory: %v", err)
39+
}
40+
testDir = dir
41+
42+
// provider install places the provider in the "$(HOME)/.config/mondoo/providers/${$@_NAME}") but we
43+
// want to test it in isolation. Therefore, we copy the provider to the current directory .providers
44+
osProviderPath := filepath.Join(testDir, "os")
45+
if err := os.MkdirAll(osProviderPath, 0755); err != nil {
46+
log.Fatalf("creating directory: %v", err)
47+
}
48+
49+
distPath, err := filepath.Abs("../../providers/os/dist")
2350
if err != nil {
24-
panic(err)
51+
log.Fatalf("unable to expand dist path: %v", err)
52+
}
53+
54+
if err := os.CopyFS(osProviderPath, os.DirFS(distPath)); err != nil {
55+
log.Fatalf("copying provider: %v", err)
2556
}
2657
}
2758

@@ -35,13 +66,15 @@ func TestCompare(t *testing.T) {
3566
ts, err := cmdtest.Read("testdata")
3667
require.NoError(t, err)
3768

69+
// Set a fake config path to avoid loading the real configuration
70+
// file from the system running this tests
71+
os.Setenv("MONDOO_CONFIG_PATH", path.Join(testDir, "foo"))
72+
// Override providers path with the fake test directory
73+
os.Setenv("PROVIDERS_PATH", testDir)
74+
// Disable auto-update to avoid installing providers
75+
os.Setenv("MONDOO_AUTO_UPDATE", "false")
76+
3877
ts.DisableLogging = true
39-
ts.Commands["cnquery"] = cmdtest.InProcessProgram("cnquery", func() int {
40-
err := cnqueryCmd.Execute()
41-
if err != nil {
42-
return 1
43-
}
44-
return 0
45-
})
78+
ts.Commands["cnquery"] = cmdtest.Program("cnquery")
4679
ts.Run(t, false) // set to true to update test files
4780
}

test/commands/testdata/cnquery_run.ct

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,30 @@ Usage:
66
cnquery run [command]
77

88
Available Commands:
9+
container Run a query with a running container or container image
10+
device Run a query with a block device target
11+
docker Run a query with a running Docker container, Docker image, or Dockerfile
12+
filesystem Run a query with a mounted file system target
13+
local Run a query with your local system
914
mock Run a query with a recording file without an active connection
1015
sbom Run a query with read SBOM file on disk
16+
ssh Run a query with a remote system via SSH
17+
vagrant Run a query with a Vagrant host
18+
winrm Run a query with a remote system via WinRM
1119

1220
Flags:
13-
--ast Parse the query and return the abstract syntax tree (AST)
14-
-c, --command string MQL query to execute in the shell
15-
-h, --help help for run
16-
--info Parse the query and provide information about it
17-
-j, --json Run the query and return the object in a JSON structure
18-
--parse Parse the query and return the logical structure
19-
--platform-id string Select a specific target asset by providing its platform ID
21+
--ast Parse the query and return the abstract syntax tree (AST)
22+
-c, --command string MQL query to execute in the shell
23+
--discover strings Enable the discovery of nested assets. Supports: all, auto, container, container-images
24+
--exit-1-on-failure Exit with error code 1 if one or more query results fail
25+
-h, --help help for run
26+
--info Parse the query and provide information about it
27+
-j, --json Run the query and return the object in a JSON structure
28+
--parse Parse the query and return the logical structure
29+
--platform-id string Select a specific target asset by providing its platform ID
30+
--record string Record all resource calls and use resources in the recording
31+
--sudo Elevate privileges with sudo
32+
--use-recording string Use a recording to inject resource data (read-only)
2033

2134
Global Flags:
2235
--api-proxy string Set proxy for communications with Mondoo Platform API

test/commands/testdata/cnquery_run_exit_1_on_failure.ct

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
$ cnquery run local -c 1==2
2+
→ no Mondoo configuration file provided, using defaults
3+
[failed] == 2
4+
expected: == 2
5+
actual: 1
6+
7+
$ cnquery run local -c 1==2 --exit-1-on-failure --> FAIL
8+
→ no Mondoo configuration file provided, using defaults
9+
[failed] == 2
10+
expected: == 2
11+
actual: 1

test/commands/testdata/cnquery_sbom.ct

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,25 @@ Usage:
1717
cnquery sbom [command]
1818

1919
Available Commands:
20+
container Collect a software bill of materials (SBOM) for a running container or container image
21+
docker Collect a software bill of materials (SBOM) for a running Docker container, Docker image, or Dockerfile
22+
filesystem Collect a software bill of materials (SBOM) for a mounted file system target
23+
local Collect a software bill of materials (SBOM) for your local system
2024
sbom Collect a software bill of materials (SBOM) for read SBOM file on disk
25+
ssh Collect a software bill of materials (SBOM) for a remote system via SSH
26+
vagrant Collect a software bill of materials (SBOM) for a Vagrant host
27+
winrm Collect a software bill of materials (SBOM) for a remote system via WinRM
2128

2229
Flags:
2330
--annotation stringToString Add an annotation to the asset (default [])
2431
--asset-name string User-override for the asset name
32+
--discover strings Enable the discovery of nested assets. Supports: all, auto, container, container-images
2533
-h, --help help for sbom
2634
-o, --output string Set output format: json, cyclonedx-json, cyclonedx-xml, spdx-json, spdx-tag-value, table (default "list")
2735
--output-target string Set output target to which the SBOM report will be written
36+
--record string Record all resource calls and use resources in the recording
37+
--sudo Elevate privileges with sudo
38+
--use-recording string Use a recording to inject resource data (read-only)
2839
--with-evidence Display evidence for each component
2940

3041
Global Flags:

test/commands/testdata/cnquery_scan.ct

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,22 @@ Usage:
1414
cnquery scan [command]
1515

1616
Available Commands:
17+
container Scan a running container or container image
18+
device Scan a block device target
19+
docker Scan a running Docker container, Docker image, or Dockerfile
20+
filesystem Scan a mounted file system target
21+
local Scan your local system
1722
mock Scan a recording file without an active connection
1823
sbom Scan read SBOM file on disk
24+
ssh Scan a remote system via SSH
25+
vagrant Scan a Vagrant host
26+
winrm Scan a remote system via WinRM
1927

2028
Flags:
2129
--annotation stringToString Add an annotation to the asset (default [])
2230
--asset-name string User-override for the asset name
2331
--detect-cicd Try to detect CI/CD environments. If detected, set the asset category to 'cicd' (default true)
32+
--discover strings Enable the discovery of nested assets. Supports: all, auto, container, container-images
2433
-h, --help help for scan
2534
--incognito Run in incognito mode. Do not report scan results to Mondoo Platform
2635
--inventory-file string Set the path to the inventory file
@@ -32,7 +41,10 @@ Flags:
3241
--props stringToString Custom values for properties (default [])
3342
--querypack querypack-bundle Set the query packs to execute. This requires querypack-bundle. You can specify multiple UIDs
3443
-f, --querypack-bundle strings Path to local query pack file
44+
--record string Record all resource calls and use resources in the recording
45+
--sudo Elevate privileges with sudo
3546
--trace-id string Trace identifier
47+
--use-recording string Use a recording to inject resource data (read-only)
3648

3749
Global Flags:
3850
--api-proxy string Set proxy for communications with Mondoo Platform API

0 commit comments

Comments
 (0)