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

split container-diff into subcommands: analyze and diff #60

Merged
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
10 changes: 5 additions & 5 deletions .container-diff-tests.sh
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
#!/bin/bash
while IFS=$' \n\r' read -r flag differ image1 image2 file; do
go run main.go $image1 $image2 $flag -j > $file
go run main.go diff $image1 $image2 $flag -j > $file
if [[ $? -ne 0 ]]; then
echo "container-diff" "$differ" "differ failed"
echo "ERROR container-diff diff $differ differ failed"
exit 1
fi
done < tests/test_differ_runs.txt

while IFS=$' \n\r' read -r flag analyzer image file; do
go run main.go $image $flag -j > $file
go run main.go analyze $image $flag -j > $file
if [[ $? -ne 0 ]]; then
echo "container-diff" "$analyzer" "analyzer failed"
echo "ERROR: container-diff analyze $analyzer analyzer failed"
exit 1
fi
done < tests/test_analyzer_runs.txt
Expand All @@ -19,7 +19,7 @@ success=0
while IFS=$' \n\r' read -r type analyzer actual expected; do
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')
if ! "$diff" ; then
echo "container-diff" "$analyzer" "$type" "output is not as expected"
echo "ERROR: container-diff analyze $analyzer $type: output is not as expected"
success=1
fi
done < tests/test_run_comparisons.txt
Expand Down
30 changes: 15 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,31 +39,31 @@ Download the [container-diff-windows-amd64.exe](https://storage.googleapis.com/c

## Quickstart

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:
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:

```
container-diff <img> [Run all analyzers]
container-diff <img> -d [History]
container-diff <img> -f [File System]
container-diff <img> -p [Pip]
container-diff <img> -a [Apt]
container-diff <img> -n [Node]
container-diff analyze <img> [Run all analyzers]
container-diff analyze <img> -d [History]
container-diff analyze <img> -f [File System]
container-diff analyze <img> -p [Pip]
container-diff analyze <img> -a [Apt]
container-diff analyze <img> -n [Node]
```

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:
```
container-diff <img1> <img2> [Run all differs]
container-diff <img1> <img2> -d [History]
container-diff <img1> <img2> -f [File System]
container-diff <img1> <img2> -p [Pip]
container-diff <img1> <img2> -a [Apt]
container-diff <img1> <img2> -n [Node]
container-diff diff <img1> <img2> [Run all differs]
container-diff diff <img1> <img2> -d [History]
container-diff diff <img1> <img2> -f [File System]
container-diff diff <img1> <img2> -p [Pip]
container-diff diff <img1> <img2> -a [Apt]
container-diff diff <img1> <img2> -n [Node]
```

You can similarly run many differs or analyzers at once:
You can similarly run many analyzers at once:

```
container-diff <img1> <img2> -d -a -n [History, Apt, and Node]
container-diff diff <img1> <img2> -d -a -n [History, Apt, and Node]
```

All of the analyzer flags with their long versions can be seen below:
Expand Down
90 changes: 90 additions & 0 deletions cmd/analyze.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package cmd

import (
"errors"
"os"

"github.com/GoogleCloudPlatform/container-diff/differs"
"github.com/GoogleCloudPlatform/container-diff/utils"
"github.com/golang/glog"
"github.com/spf13/cobra"
)

var analyzeCmd = &cobra.Command{
Use: "analyze",
Short: "Analyzes an image: [image]",
Long: `Analyzes an image using the specifed analyzers as indicated via flags (see documentation for available ones).`,
Run: func(cmd *cobra.Command, args []string) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming this is just a copy-paste from the analyze and diff functions? So no need to do a full review of the code?

if validArgs, err := validateArgs(args, checkAnalyzeArgNum, checkArgType); !validArgs {
glog.Error(err.Error())
os.Exit(1)
}

utils.SetDockerEngine(eng)

analyzeArgs := []string{}
allAnalyzers := getAllAnalyzers()
for _, name := range allAnalyzers {
if *analyzeFlagMap[name] == true {
analyzeArgs = append(analyzeArgs, name)
}
}

// If no analyzers are specified, perform them all as the default
if len(analyzeArgs) == 0 {
analyzeArgs = allAnalyzers
}

if err := analyzeImage(args[0], analyzeArgs); err != nil {
glog.Error(err)
os.Exit(1)
}
},
}

func checkAnalyzeArgNum(args []string) (bool, error) {
var errMessage string
if len(args) != 1 {
errMessage = "'analyze' requires one image as an argument: container analyze [image]"
return false, errors.New(errMessage)
}
return true, nil
}

func analyzeImage(imageArg string, analyzerArgs []string) error {
image, err := utils.ImagePrepper{imageArg}.GetImage()
if err != nil {
glog.Error(err.Error())
cleanupImage(image)
return errors.New("Could not perform image analysis")
}
analyzeTypes, err := differs.GetAnalyzers(analyzerArgs)
if err != nil {
glog.Error(err.Error())
cleanupImage(image)
return errors.New("Could not perform image analysis")
}

req := differs.SingleRequest{image, analyzeTypes}
if analyses, err := req.GetAnalysis(); err == nil {
glog.Info("Retrieving analyses")
outputResults(analyses)
if !save {
cleanupImage(image)
} else {
dir, _ := os.Getwd()
glog.Infof("Image was saved at %s as %s", dir, image.FSPath)
}
} else {
glog.Error(err.Error())
cleanupImage(image)
return errors.New("Could not perform image analysis")
}

return nil
}

func init() {
RootCmd.AddCommand(analyzeCmd)
addSharedFlags(analyzeCmd)
}
24 changes: 24 additions & 0 deletions cmd/analyze_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package cmd

import (
"testing"
)

var analyzeArgNumTests = []testpair{
{[]string{}, false},
{[]string{"one"}, true},
{[]string{"one", "two"}, false},
}

func TestAnalyzeArgNum(t *testing.T) {
for _, test := range analyzeArgNumTests {
valid, err := checkAnalyzeArgNum(test.input)
if valid != test.expected_output {
if test.expected_output {
t.Errorf("Got unexpected error: %s", err)
} else {
t.Errorf("Expected error but got none")
}
}
}
}
118 changes: 118 additions & 0 deletions cmd/diff.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package cmd

import (
"errors"
"os"
"sync"

"github.com/GoogleCloudPlatform/container-diff/differs"
"github.com/GoogleCloudPlatform/container-diff/utils"
"github.com/golang/glog"
"github.com/spf13/cobra"
)

var diffCmd = &cobra.Command{
Use: "diff",
Short: "Compare two images: [image1] [image2]",
Long: `Compares two images using the specifed analyzers as indicated via flags (see documentation for available ones).`,
Run: func(cmd *cobra.Command, args []string) {
if validArgs, err := validateArgs(args, checkDiffArgNum, checkArgType); !validArgs {
glog.Error(err.Error())
os.Exit(1)
}

utils.SetDockerEngine(eng)

analyzeArgs := []string{}
allAnalyzers := getAllAnalyzers()
for _, name := range allAnalyzers {
if *analyzeFlagMap[name] == true {
analyzeArgs = append(analyzeArgs, name)
}
}

// If no analyzers are specified, perform them all as the default
if len(analyzeArgs) == 0 {
analyzeArgs = allAnalyzers
}

if err := diffImages(args[0], args[1], analyzeArgs); err != nil {
glog.Error(err)
os.Exit(1)
}
},
}

func checkDiffArgNum(args []string) (bool, error) {
var errMessage string
if len(args) != 2 {
errMessage = "'diff' requires two images as arguments: container diff [image1] [image2]"
return false, errors.New(errMessage)
}
return true, nil
}

func diffImages(image1Arg, image2Arg string, diffArgs []string) error {
var wg sync.WaitGroup
wg.Add(2)

glog.Infof("Starting diff on images %s and %s, using differs: %s", image1Arg, image2Arg, diffArgs)

var image1, image2 utils.Image
var err error
go func() {
defer wg.Done()
image1, err = utils.ImagePrepper{image1Arg}.GetImage()
if err != nil {
glog.Error(err.Error())
}
}()

go func() {
defer wg.Done()
image2, err = utils.ImagePrepper{image2Arg}.GetImage()
if err != nil {
glog.Error(err.Error())
}
}()
wg.Wait()
if err != nil {
cleanupImage(image1)
cleanupImage(image2)
return errors.New("Could not perform image diff")
}

diffTypes, err := differs.GetAnalyzers(diffArgs)
if err != nil {
glog.Error(err.Error())
cleanupImage(image1)
cleanupImage(image2)
return errors.New("Could not perform image diff")
}

req := differs.DiffRequest{image1, image2, diffTypes}
if diffs, err := req.GetDiff(); err == nil {
glog.Info("Retrieving diffs")
outputResults(diffs)
if !save {
cleanupImage(image1)
cleanupImage(image2)

} else {
dir, _ := os.Getwd()
glog.Infof("Images were saved at %s as %s and %s", dir, image1.FSPath, image2.FSPath)
}
} else {
glog.Error(err.Error())
cleanupImage(image1)
cleanupImage(image2)
return errors.New("Could not perform image diff")
}

return nil
}

func init() {
RootCmd.AddCommand(diffCmd)
addSharedFlags(diffCmd)
}
25 changes: 25 additions & 0 deletions cmd/diff_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package cmd

import (
"testing"
)

var diffArgNumTests = []testpair{
{[]string{}, false},
{[]string{"one"}, false},
{[]string{"one", "two"}, true},
{[]string{"one", "two", "three"}, false},
}

func TestDiffArgNum(t *testing.T) {
for _, test := range diffArgNumTests {
valid, err := checkDiffArgNum(test.input)
if valid != test.expected_output {
if test.expected_output {
t.Errorf("Got unexpected error: %s", err)
} else {
t.Errorf("Expected error but got none")
}
}
}
}
Loading