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

Changed CLI to use --types flag, a comma separated list of desired an… #68

Merged
merged 1 commit into from
Sep 6, 2017
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
30 changes: 14 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,27 +43,29 @@ To use `container-diff analyze` to perform analysis on a single image, you need

```
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]
container-diff analyze <img> --types=history [History]
container-diff analyze <img> --types=file [File System]
container-diff analyze <img> --types=pip [Pip]
container-diff analyze <img> --types=apt [Apt]
container-diff analyze <img> --types=node [Node]
container-diff analyze <img> --types=apt,node [Apt and Node]
# --types=<analyzer1>,<analyzer2>,<analyzer3>,...
```

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 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]
container-diff diff <img1> <img2> --types=history [History]
container-diff diff <img1> <img2> --types=file [File System]
container-diff diff <img1> <img2> --types=pip [Pip]
container-diff diff <img1> <img2> --types=apt [Apt]
container-diff diff <img1> <img2> --types=node [Node]
```

You can similarly run many analyzers at once:

```
container-diff diff <img1> <img2> -d -a -n [History, Apt, and Node]
container-diff diff <img1> <img2> --types=history,apt,node [History, Apt, and Node]
```

All of the analyzer flags with their long versions can be seen below:
Expand Down Expand Up @@ -228,7 +230,7 @@ To run container-diff on image IDs, docker must be installed.
## Example Run

```
$ container-diff gcr.io/google-appengine/python:2017-07-21-123058 gcr.io/google-appengine/python:2017-06-29-190410 -a -n -p
$ container-diff diff gcr.io/google-appengine/python:2017-07-21-123058 gcr.io/google-appengine/python:2017-06-29-190410 --types=apt,node,pip

-----AptDiffer-----

Expand Down Expand Up @@ -372,7 +374,3 @@ This is where you define how your analyzer should output for a human readable fo

5. Add your analyzer to the `analyses` map in [differs.go](https://github.com/GoogleCloudPlatform/container-diff/blob/master/differs/differs.go#L22) with the corresponding Analyzer struct as the value.





27 changes: 10 additions & 17 deletions cmd/analyze.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"os"
"strings"

"github.com/GoogleCloudPlatform/container-diff/differs"
"github.com/GoogleCloudPlatform/container-diff/utils"
Expand All @@ -16,37 +17,29 @@ var analyzeCmd = &cobra.Command{
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) {
if validArgs, err := validateArgs(args, checkAnalyzeArgNum, checkArgType); !validArgs {
if err := validateArgs(args, checkAnalyzeArgNum, checkArgType); err != nil {
glog.Error(err.Error())
os.Exit(1)
}
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 := checkIfValidAnalyzer(types); err != nil {
glog.Error(err)
os.Exit(1)
}

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

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

func analyzeImage(imageArg string, analyzerArgs []string) error {
Expand Down
4 changes: 2 additions & 2 deletions cmd/analyze_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ var analyzeArgNumTests = []testpair{

func TestAnalyzeArgNum(t *testing.T) {
for _, test := range analyzeArgNumTests {
valid, err := checkAnalyzeArgNum(test.input)
if valid != test.expected_output {
err := checkAnalyzeArgNum(test.input)
if (err == nil) != test.expected_output {
if test.expected_output {
t.Errorf("Got unexpected error: %s", err)
} else {
Expand Down
26 changes: 9 additions & 17 deletions cmd/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"os"
"strings"
"sync"

"github.com/GoogleCloudPlatform/container-diff/differs"
Expand All @@ -17,37 +18,28 @@ var diffCmd = &cobra.Command{
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 {
if err := validateArgs(args, checkDiffArgNum, checkArgType); err != nil {
glog.Error(err.Error())
os.Exit(1)
}
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 := checkIfValidAnalyzer(types); err != nil {
glog.Error(err)
os.Exit(1)
}

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

func checkDiffArgNum(args []string) (bool, error) {
func checkDiffArgNum(args []string) 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 errors.New(errMessage)
}
return true, nil
return nil
}

func diffImages(image1Arg, image2Arg string, diffArgs []string) error {
Expand Down
4 changes: 2 additions & 2 deletions cmd/diff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ var diffArgNumTests = []testpair{

func TestDiffArgNum(t *testing.T) {
for _, test := range diffArgNumTests {
valid, err := checkDiffArgNum(test.input)
if valid != test.expected_output {
err := checkDiffArgNum(test.input)
if (err == nil) != test.expected_output {
if test.expected_output {
t.Errorf("Got unexpected error: %s", err)
} else {
Expand Down
70 changes: 25 additions & 45 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,40 +1,28 @@
package cmd

import (
"bytes"
"context"
"errors"
goflag "flag"
"fmt"
"os"
"reflect"
"sort"
"strings"

"github.com/GoogleCloudPlatform/container-diff/differs"
"github.com/GoogleCloudPlatform/container-diff/utils"
"github.com/docker/docker/client"
"github.com/golang/glog"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

var json bool
var eng bool
var save bool
var apt bool
var node bool
var file bool
var history bool
var pip bool

var analyzeFlagMap = map[string]*bool{
"apt": &apt,
"node": &node,
"file": &file,
"history": &history,
"pip": &pip,
}
var types string

type validatefxn func(args []string) (bool, error)
type validatefxn func(args []string) error

var RootCmd = &cobra.Command{
Use: "container-diff",
Expand Down Expand Up @@ -90,24 +78,13 @@ func cleanupImage(image utils.Image) {
}
}

func getAllAnalyzers() []string {
allAnalyzers := []string{}
for name := range analyzeFlagMap {
allAnalyzers = append(allAnalyzers, name)
}
return allAnalyzers
}

func validateArgs(args []string, validatefxns ...validatefxn) (bool, error) {
func validateArgs(args []string, validatefxns ...validatefxn) error {
for _, validatefxn := range validatefxns {
valid, err := validatefxn(args)
if err != nil {
return false, err
} else if !valid {
return false, nil
if err := validatefxn(args); err != nil {
return err
}
}
return true, nil
return nil
}

func checkImage(arg string) bool {
Expand All @@ -117,20 +94,27 @@ func checkImage(arg string) bool {
return true
}

func checkArgType(args []string) (bool, error) {
var buffer bytes.Buffer
valid := true
func checkArgType(args []string) error {
for _, arg := range args {
if !checkImage(arg) {
valid = false
errMessage := fmt.Sprintf("Argument %s is not an image ID, URL, or tar\n", args[0])
buffer.WriteString(errMessage)
glog.Errorf(errMessage)
return errors.New(errMessage)
}
}
if !valid {
return false, errors.New(buffer.String())
return nil
}

func checkIfValidAnalyzer(flagtypes string) error {
analyzers := strings.Split(flagtypes, ",")
for _, name := range analyzers {
if _, exists := differs.Analyzers[name]; !exists {
errMessage := fmt.Sprintf("Argument %s is not an image ID, URL, or tar\n", name)
glog.Errorf(errMessage)
return errors.New(errMessage)
}
}
return true, nil
return nil
}

func remove(path string, dir bool) string {
Expand All @@ -157,11 +141,7 @@ func init() {

func addSharedFlags(cmd *cobra.Command) {
cmd.Flags().BoolVarP(&json, "json", "j", false, "JSON Output defines if the diff should be returned in a human readable format (false) or a JSON (true).")
cmd.Flags().BoolVarP(&pip, "pip", "p", false, "Set this flag to use the pip differ.")
cmd.Flags().BoolVarP(&node, "node", "n", false, "Set this flag to use the node differ.")
cmd.Flags().BoolVarP(&apt, "apt", "a", false, "Set this flag to use the apt differ.")
cmd.Flags().BoolVarP(&file, "file", "f", false, "Set this flag to use the file differ.")
cmd.Flags().BoolVarP(&history, "history", "d", false, "Set this flag to use the dockerfile history differ.")
cmd.Flags().StringVarP(&types, "types", "t", "", "This flag sets the list of analyzer types to use. It expects a comma separated list of supported analyzers.")
cmd.Flags().BoolVarP(&save, "save", "s", false, "Set this flag to save rather than remove the final image filesystems on exit.")
cmd.Flags().BoolVarP(&utils.SortSize, "order", "o", false, "Set this flag to sort any file/package results by descending size. Otherwise, they will be sorted by name.")
}
4 changes: 2 additions & 2 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ var argTypeTests = []testpair{

func TestArgType(t *testing.T) {
for _, test := range argTypeTests {
valid, err := checkArgType(test.input)
if valid != test.expected_output {
err := checkArgType(test.input)
if (err == nil) != test.expected_output {
if test.expected_output {
t.Errorf("Got unexpected error: %s", err)
} else {
Expand Down
4 changes: 2 additions & 2 deletions differs/differs.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type Analyzer interface {
Analyze(image utils.Image) (utils.Result, error)
}

var analyzers = map[string]Analyzer{
var Analyzers = map[string]Analyzer{
"history": HistoryAnalyzer{},
"file": FileAnalyzer{},
"apt": AptAnalyzer{},
Expand Down Expand Up @@ -84,7 +84,7 @@ func (req SingleRequest) GetAnalysis() (map[string]utils.Result, error) {

func GetAnalyzers(analyzeNames []string) (analyzeFuncs []Analyzer, err error) {
for _, name := range analyzeNames {
if a, exists := analyzers[name]; exists {
if a, exists := Analyzers[name]; exists {
analyzeFuncs = append(analyzeFuncs, a)
} else {
glog.Errorf("Unknown analyzer/differ specified", name)
Expand Down
Loading