Skip to content
This repository was archived by the owner on Mar 27, 2024. It is now read-only.

Commit 828be57

Browse files
Merge pull request #60 from aaron-prindle/add-subcommands
split container-diff into subcommands: analyze and diff
2 parents 3aecaf9 + ad82215 commit 828be57

File tree

8 files changed

+299
-205
lines changed

8 files changed

+299
-205
lines changed

.container-diff-tests.sh

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
#!/bin/bash
22
while IFS=$' \n\r' read -r flag differ image1 image2 file; do
3-
go run main.go $image1 $image2 $flag -j > $file
3+
go run main.go diff $image1 $image2 $flag -j > $file
44
if [[ $? -ne 0 ]]; then
5-
echo "container-diff" "$differ" "differ failed"
5+
echo "ERROR container-diff diff $differ differ failed"
66
exit 1
77
fi
88
done < tests/test_differ_runs.txt
99

1010
while IFS=$' \n\r' read -r flag analyzer image file; do
11-
go run main.go $image $flag -j > $file
11+
go run main.go analyze $image $flag -j > $file
1212
if [[ $? -ne 0 ]]; then
13-
echo "container-diff" "$analyzer" "analyzer failed"
13+
echo "ERROR: container-diff analyze $analyzer analyzer failed"
1414
exit 1
1515
fi
1616
done < tests/test_analyzer_runs.txt
@@ -19,7 +19,7 @@ success=0
1919
while IFS=$' \n\r' read -r type analyzer actual expected; do
2020
diff=$(jq --argfile a "$actual" --argfile b "$expected" -n 'def walk(f): . as $in | if type == "object" then reduce keys[] as $key ( {}; . + { ($key): ($in[$key] | walk(f)) } ) | f elif type == "array" then map( walk(f) ) | f else f end; ($a | walk(if type == "array" then sort else . end)) as $a | ($b | walk(if type == "array" then sort else . end)) as $b | $a == $b')
2121
if ! "$diff" ; then
22-
echo "container-diff" "$analyzer" "$type" "output is not as expected"
22+
echo "ERROR: container-diff analyze $analyzer $type: output is not as expected"
2323
success=1
2424
fi
2525
done < tests/test_run_comparisons.txt

README.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,31 +39,31 @@ Download the [container-diff-windows-amd64.exe](https://storage.googleapis.com/c
3939

4040
## Quickstart
4141

42-
To use container-diff to perform analysis on a single image, you need one Docker image (in the form of an ID, tarball, or URL from a repo). Once you have that image, you can run any of the following analyzers:
42+
To use `container-diff analyze` to perform analysis on a single image, you need one Docker image (in the form of an ID, tarball, or URL from a repo). Once you have that image, you can run any of the following analyzers:
4343

4444
```
45-
container-diff <img> [Run all analyzers]
46-
container-diff <img> -d [History]
47-
container-diff <img> -f [File System]
48-
container-diff <img> -p [Pip]
49-
container-diff <img> -a [Apt]
50-
container-diff <img> -n [Node]
45+
container-diff analyze <img> [Run all analyzers]
46+
container-diff analyze <img> -d [History]
47+
container-diff analyze <img> -f [File System]
48+
container-diff analyze <img> -p [Pip]
49+
container-diff analyze <img> -a [Apt]
50+
container-diff analyze <img> -n [Node]
5151
```
5252

5353
To use container-diff to perform a diff analysis on two images, you need two Docker images (in the form of an ID, tarball, or URL from a repo). Once you have those images, you can run any of the following differs:
5454
```
55-
container-diff <img1> <img2> [Run all differs]
56-
container-diff <img1> <img2> -d [History]
57-
container-diff <img1> <img2> -f [File System]
58-
container-diff <img1> <img2> -p [Pip]
59-
container-diff <img1> <img2> -a [Apt]
60-
container-diff <img1> <img2> -n [Node]
55+
container-diff diff <img1> <img2> [Run all differs]
56+
container-diff diff <img1> <img2> -d [History]
57+
container-diff diff <img1> <img2> -f [File System]
58+
container-diff diff <img1> <img2> -p [Pip]
59+
container-diff diff <img1> <img2> -a [Apt]
60+
container-diff diff <img1> <img2> -n [Node]
6161
```
6262

63-
You can similarly run many differs or analyzers at once:
63+
You can similarly run many analyzers at once:
6464

6565
```
66-
container-diff <img1> <img2> -d -a -n [History, Apt, and Node]
66+
container-diff diff <img1> <img2> -d -a -n [History, Apt, and Node]
6767
```
6868

6969
All of the analyzer flags with their long versions can be seen below:

cmd/analyze.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package cmd
2+
3+
import (
4+
"errors"
5+
"os"
6+
7+
"github.com/GoogleCloudPlatform/container-diff/differs"
8+
"github.com/GoogleCloudPlatform/container-diff/utils"
9+
"github.com/golang/glog"
10+
"github.com/spf13/cobra"
11+
)
12+
13+
var analyzeCmd = &cobra.Command{
14+
Use: "analyze",
15+
Short: "Analyzes an image: [image]",
16+
Long: `Analyzes an image using the specifed analyzers as indicated via flags (see documentation for available ones).`,
17+
Run: func(cmd *cobra.Command, args []string) {
18+
if validArgs, err := validateArgs(args, checkAnalyzeArgNum, checkArgType); !validArgs {
19+
glog.Error(err.Error())
20+
os.Exit(1)
21+
}
22+
23+
utils.SetDockerEngine(eng)
24+
25+
analyzeArgs := []string{}
26+
allAnalyzers := getAllAnalyzers()
27+
for _, name := range allAnalyzers {
28+
if *analyzeFlagMap[name] == true {
29+
analyzeArgs = append(analyzeArgs, name)
30+
}
31+
}
32+
33+
// If no analyzers are specified, perform them all as the default
34+
if len(analyzeArgs) == 0 {
35+
analyzeArgs = allAnalyzers
36+
}
37+
38+
if err := analyzeImage(args[0], analyzeArgs); err != nil {
39+
glog.Error(err)
40+
os.Exit(1)
41+
}
42+
},
43+
}
44+
45+
func checkAnalyzeArgNum(args []string) (bool, error) {
46+
var errMessage string
47+
if len(args) != 1 {
48+
errMessage = "'analyze' requires one image as an argument: container analyze [image]"
49+
return false, errors.New(errMessage)
50+
}
51+
return true, nil
52+
}
53+
54+
func analyzeImage(imageArg string, analyzerArgs []string) error {
55+
image, err := utils.ImagePrepper{imageArg}.GetImage()
56+
if err != nil {
57+
glog.Error(err.Error())
58+
cleanupImage(image)
59+
return errors.New("Could not perform image analysis")
60+
}
61+
analyzeTypes, err := differs.GetAnalyzers(analyzerArgs)
62+
if err != nil {
63+
glog.Error(err.Error())
64+
cleanupImage(image)
65+
return errors.New("Could not perform image analysis")
66+
}
67+
68+
req := differs.SingleRequest{image, analyzeTypes}
69+
if analyses, err := req.GetAnalysis(); err == nil {
70+
glog.Info("Retrieving analyses")
71+
outputResults(analyses)
72+
if !save {
73+
cleanupImage(image)
74+
} else {
75+
dir, _ := os.Getwd()
76+
glog.Infof("Image was saved at %s as %s", dir, image.FSPath)
77+
}
78+
} else {
79+
glog.Error(err.Error())
80+
cleanupImage(image)
81+
return errors.New("Could not perform image analysis")
82+
}
83+
84+
return nil
85+
}
86+
87+
func init() {
88+
RootCmd.AddCommand(analyzeCmd)
89+
addSharedFlags(analyzeCmd)
90+
}

cmd/analyze_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package cmd
2+
3+
import (
4+
"testing"
5+
)
6+
7+
var analyzeArgNumTests = []testpair{
8+
{[]string{}, false},
9+
{[]string{"one"}, true},
10+
{[]string{"one", "two"}, false},
11+
}
12+
13+
func TestAnalyzeArgNum(t *testing.T) {
14+
for _, test := range analyzeArgNumTests {
15+
valid, err := checkAnalyzeArgNum(test.input)
16+
if valid != test.expected_output {
17+
if test.expected_output {
18+
t.Errorf("Got unexpected error: %s", err)
19+
} else {
20+
t.Errorf("Expected error but got none")
21+
}
22+
}
23+
}
24+
}

cmd/diff.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package cmd
2+
3+
import (
4+
"errors"
5+
"os"
6+
"sync"
7+
8+
"github.com/GoogleCloudPlatform/container-diff/differs"
9+
"github.com/GoogleCloudPlatform/container-diff/utils"
10+
"github.com/golang/glog"
11+
"github.com/spf13/cobra"
12+
)
13+
14+
var diffCmd = &cobra.Command{
15+
Use: "diff",
16+
Short: "Compare two images: [image1] [image2]",
17+
Long: `Compares two images using the specifed analyzers as indicated via flags (see documentation for available ones).`,
18+
Run: func(cmd *cobra.Command, args []string) {
19+
if validArgs, err := validateArgs(args, checkDiffArgNum, checkArgType); !validArgs {
20+
glog.Error(err.Error())
21+
os.Exit(1)
22+
}
23+
24+
utils.SetDockerEngine(eng)
25+
26+
analyzeArgs := []string{}
27+
allAnalyzers := getAllAnalyzers()
28+
for _, name := range allAnalyzers {
29+
if *analyzeFlagMap[name] == true {
30+
analyzeArgs = append(analyzeArgs, name)
31+
}
32+
}
33+
34+
// If no analyzers are specified, perform them all as the default
35+
if len(analyzeArgs) == 0 {
36+
analyzeArgs = allAnalyzers
37+
}
38+
39+
if err := diffImages(args[0], args[1], analyzeArgs); err != nil {
40+
glog.Error(err)
41+
os.Exit(1)
42+
}
43+
},
44+
}
45+
46+
func checkDiffArgNum(args []string) (bool, error) {
47+
var errMessage string
48+
if len(args) != 2 {
49+
errMessage = "'diff' requires two images as arguments: container diff [image1] [image2]"
50+
return false, errors.New(errMessage)
51+
}
52+
return true, nil
53+
}
54+
55+
func diffImages(image1Arg, image2Arg string, diffArgs []string) error {
56+
var wg sync.WaitGroup
57+
wg.Add(2)
58+
59+
glog.Infof("Starting diff on images %s and %s, using differs: %s", image1Arg, image2Arg, diffArgs)
60+
61+
var image1, image2 utils.Image
62+
var err error
63+
go func() {
64+
defer wg.Done()
65+
image1, err = utils.ImagePrepper{image1Arg}.GetImage()
66+
if err != nil {
67+
glog.Error(err.Error())
68+
}
69+
}()
70+
71+
go func() {
72+
defer wg.Done()
73+
image2, err = utils.ImagePrepper{image2Arg}.GetImage()
74+
if err != nil {
75+
glog.Error(err.Error())
76+
}
77+
}()
78+
wg.Wait()
79+
if err != nil {
80+
cleanupImage(image1)
81+
cleanupImage(image2)
82+
return errors.New("Could not perform image diff")
83+
}
84+
85+
diffTypes, err := differs.GetAnalyzers(diffArgs)
86+
if err != nil {
87+
glog.Error(err.Error())
88+
cleanupImage(image1)
89+
cleanupImage(image2)
90+
return errors.New("Could not perform image diff")
91+
}
92+
93+
req := differs.DiffRequest{image1, image2, diffTypes}
94+
if diffs, err := req.GetDiff(); err == nil {
95+
glog.Info("Retrieving diffs")
96+
outputResults(diffs)
97+
if !save {
98+
cleanupImage(image1)
99+
cleanupImage(image2)
100+
101+
} else {
102+
dir, _ := os.Getwd()
103+
glog.Infof("Images were saved at %s as %s and %s", dir, image1.FSPath, image2.FSPath)
104+
}
105+
} else {
106+
glog.Error(err.Error())
107+
cleanupImage(image1)
108+
cleanupImage(image2)
109+
return errors.New("Could not perform image diff")
110+
}
111+
112+
return nil
113+
}
114+
115+
func init() {
116+
RootCmd.AddCommand(diffCmd)
117+
addSharedFlags(diffCmd)
118+
}

cmd/diff_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package cmd
2+
3+
import (
4+
"testing"
5+
)
6+
7+
var diffArgNumTests = []testpair{
8+
{[]string{}, false},
9+
{[]string{"one"}, false},
10+
{[]string{"one", "two"}, true},
11+
{[]string{"one", "two", "three"}, false},
12+
}
13+
14+
func TestDiffArgNum(t *testing.T) {
15+
for _, test := range diffArgNumTests {
16+
valid, err := checkDiffArgNum(test.input)
17+
if valid != test.expected_output {
18+
if test.expected_output {
19+
t.Errorf("Got unexpected error: %s", err)
20+
} else {
21+
t.Errorf("Expected error but got none")
22+
}
23+
}
24+
}
25+
}

0 commit comments

Comments
 (0)