From 1f50d17ff8cab76d561247332d08f349a1a0cbdc Mon Sep 17 00:00:00 2001 From: David Cassany Date: Tue, 10 Jul 2018 18:18:05 +0200 Subject: [PATCH 1/4] Add layer analysis for single version packages This commit implements the interfaces to perform package differs on layers. For diff command it compares the packages of each image on each layer, one by one. For analyze command it diffs the packages of each layer within the previous one (layers without packages changes are omitted). Signed-off-by: David Cassany --- differs/package_differs.go | 79 ++++++++++++++++++++++++++++++++++++ util/analyze_output_utils.go | 72 ++++++++++++++++++++++++++++++++ util/diff_output_utils.go | 66 ++++++++++++++++++++++++++++++ util/format_utils.go | 26 ++++++------ util/package_diff_utils.go | 6 +++ util/template_utils.go | 32 +++++++++++++++ 6 files changed, 269 insertions(+), 12 deletions(-) diff --git a/differs/package_differs.go b/differs/package_differs.go index d9e3da2c..7809ee16 100644 --- a/differs/package_differs.go +++ b/differs/package_differs.go @@ -21,6 +21,7 @@ import ( pkgutil "github.com/GoogleContainerTools/container-diff/pkg/util" "github.com/GoogleContainerTools/container-diff/util" + "github.com/sirupsen/logrus" ) type MultiVersionPackageAnalyzer interface { @@ -33,6 +34,11 @@ type SingleVersionPackageAnalyzer interface { Name() string } +type SingleVersionPackageLayerAnalyzer interface { + getPackages(image pkgutil.Image) ([]map[string]util.PackageInfo, error) + Name() string +} + func multiVersionDiff(image1, image2 pkgutil.Image, differ MultiVersionPackageAnalyzer) (*util.MultiVersionPackageDiffResult, error) { pack1, err := differ.getPackages(image1) if err != nil { @@ -71,6 +77,43 @@ func singleVersionDiff(image1, image2 pkgutil.Image, differ SingleVersionPackage }, nil } +// singleVersionLayerDiff diffs the packages of each image layer by layer +func singleVersionLayerDiff(image1, image2 pkgutil.Image, differ SingleVersionPackageLayerAnalyzer) (*util.SingleVersionPackageLayerDiffResult, error) { + pack1, err := differ.getPackages(image1) + if err != nil { + return &util.SingleVersionPackageLayerDiffResult{}, err + } + pack2, err := differ.getPackages(image2) + if err != nil { + return &util.SingleVersionPackageLayerDiffResult{}, err + } + var pkgDiffs []util.PackageDiff + + // Go through each layer for image1 + for i := range pack1 { + if i >= len(pack2) { + // Skip diff when there is no layer to compare with in image2 + continue + } + + pkgDiff := util.GetMapDiff(pack1[i], pack2[i]) + pkgDiffs = append(pkgDiffs, pkgDiff) + } + + if len(image1.Layers) != len(image2.Layers) { + logrus.Infof("%s and %s have different number of layers, please consider using container-diff analyze to view the contents of each image in each layer", image1.Source, image2.Source) + } + + return &util.SingleVersionPackageLayerDiffResult{ + Image1: image1.Source, + Image2: image2.Source, + DiffType: strings.TrimSuffix(differ.Name(), "Analyzer"), + Diff: util.PackageLayerDiff{ + PackageDiffs: pkgDiffs, + }, + }, nil +} + func multiVersionAnalysis(image pkgutil.Image, analyzer MultiVersionPackageAnalyzer) (*util.MultiVersionPackageAnalyzeResult, error) { pack, err := analyzer.getPackages(image) if err != nil { @@ -98,3 +141,39 @@ func singleVersionAnalysis(image pkgutil.Image, analyzer SingleVersionPackageAna } return &analysis, nil } + +// singleVersionLayerAnalysis returns the packages included, deleted or +// updated in each layer +func singleVersionLayerAnalysis(image pkgutil.Image, analyzer SingleVersionPackageLayerAnalyzer) (*util.SingleVersionPackageLayerAnalyzeResult, error) { + pack, err := analyzer.getPackages(image) + if err != nil { + return &util.SingleVersionPackageLayerAnalyzeResult{}, err + } + var pkgDiffs []util.PackageDiff + + // Each layer with modified packages includes a complete list of packages + // in its package database. Thus we diff the current layer with the + // previous one. Not all layers may include differences in packages, those + // are omitted. + preInd := -1 + for i := range pack { + var pkgDiff util.PackageDiff + if preInd < 0 && len(pack[i]) > 0 { + pkgDiff = util.GetMapDiff(make(map[string]util.PackageInfo), pack[i]) + preInd = i + } else if preInd >= 0 && len(pack[i]) > 0 { + pkgDiff = util.GetMapDiff(pack[preInd], pack[i]) + preInd = i + } + + pkgDiffs = append(pkgDiffs, pkgDiff) + } + + return &util.SingleVersionPackageLayerAnalyzeResult{ + Image: image.Source, + AnalyzeType: strings.TrimSuffix(analyzer.Name(), "Analyzer"), + Analysis: util.PackageLayerDiff{ + PackageDiffs: pkgDiffs, + }, + }, nil +} diff --git a/util/analyze_output_utils.go b/util/analyze_output_utils.go index 7f07ee14..3c7a006e 100644 --- a/util/analyze_output_utils.go +++ b/util/analyze_output_utils.go @@ -136,6 +136,78 @@ func (r SingleVersionPackageAnalyzeResult) OutputText(diffType string, format st return TemplateOutputFromFormat(strResult, "SingleVersionPackageAnalyze", format) } +type SingleVersionPackageLayerAnalyzeResult AnalyzeResult + +func (r SingleVersionPackageLayerAnalyzeResult) OutputStruct() interface{} { + analysis, valid := r.Analysis.(PackageLayerDiff) + if !valid { + logrus.Error("Unexpected structure of Analysis. Should be of type PackageLayerDiff") + return fmt.Errorf("Could not output %s analysis result", r.AnalyzeType) + } + + type PkgDiff struct { + Packages1 []PackageOutput + Packages2 []PackageOutput + InfoDiff []Info + } + + var analysisOutput []PkgDiff + for _, d := range analysis.PackageDiffs { + diffOutput := PkgDiff{ + Packages1: getSingleVersionPackageOutput(d.Packages1), + Packages2: getSingleVersionPackageOutput(d.Packages2), + InfoDiff: getSingleVersionInfoDiffOutput(d.InfoDiff), + } + analysisOutput = append(analysisOutput, diffOutput) + } + + output := struct { + Image string + AnalyzeType string + Analysis []PkgDiff + }{ + Image: r.Image, + AnalyzeType: r.AnalyzeType, + Analysis: analysisOutput, + } + return output +} + +func (r SingleVersionPackageLayerAnalyzeResult) OutputText(diffType string, format string) error { + analysis, valid := r.Analysis.(PackageLayerDiff) + if !valid { + logrus.Error("Unexpected structure of Analysis. Should be of type PackageLayerDiff") + return fmt.Errorf("Could not output %s analysis result", r.AnalyzeType) + } + + type StrDiff struct { + Packages1 []StrPackageOutput + Packages2 []StrPackageOutput + InfoDiff []StrInfo + } + + var analysisOutput []StrDiff + for _, d := range analysis.PackageDiffs { + diffOutput := StrDiff{ + Packages1: stringifyPackages(getSingleVersionPackageOutput(d.Packages1)), + Packages2: stringifyPackages(getSingleVersionPackageOutput(d.Packages2)), + InfoDiff: stringifyPackageDiff(getSingleVersionInfoDiffOutput(d.InfoDiff)), + } + analysisOutput = append(analysisOutput, diffOutput) + } + + strResult := struct { + Image string + AnalyzeType string + Analysis []StrDiff + }{ + Image: r.Image, + AnalyzeType: r.AnalyzeType, + Analysis: analysisOutput, + } + return TemplateOutputFromFormat(strResult, "SingleVersionPackageLayerAnalyze", format) +} + type PackageOutput struct { Name string Path string `json:",omitempty"` diff --git a/util/diff_output_utils.go b/util/diff_output_utils.go index 4f2a72c0..c8e58b55 100644 --- a/util/diff_output_utils.go +++ b/util/diff_output_utils.go @@ -162,6 +162,72 @@ func getSingleVersionInfoDiffOutput(infoDiff []Info) []Info { return infoDiff } +type SingleVersionPackageLayerDiffResult DiffResult + +func (r SingleVersionPackageLayerDiffResult) OutputStruct() interface{} { + diff, valid := r.Diff.(PackageLayerDiff) + if !valid { + logrus.Error("Unexpected structure of Diff. Should follow the PackageLayerDiff struct") + return fmt.Errorf("Could not output %s diff result", r.DiffType) + } + + type PkgDiff struct { + Packages1 []PackageOutput + Packages2 []PackageOutput + InfoDiff []Info + } + + var diffOutputs []PkgDiff + for _, d := range diff.PackageDiffs { + diffOutput := PkgDiff{ + Packages1: getSingleVersionPackageOutput(d.Packages1), + Packages2: getSingleVersionPackageOutput(d.Packages2), + InfoDiff: getSingleVersionInfoDiffOutput(d.InfoDiff), + } + diffOutputs = append(diffOutputs, diffOutput) + } + + r.Diff = diffOutputs + return r +} + +func (r SingleVersionPackageLayerDiffResult) OutputText(diffType string, format string) error { + diff, valid := r.Diff.(PackageLayerDiff) + if !valid { + logrus.Error("Unexpected structure of Diff. Should follow the PackageLayerDiff struct") + return fmt.Errorf("Could not output %s diff result", r.DiffType) + } + + type StrDiff struct { + Packages1 []StrPackageOutput + Packages2 []StrPackageOutput + InfoDiff []StrInfo + } + + var diffOutputs []StrDiff + for _, d := range diff.PackageDiffs { + diffOutput := StrDiff{ + Packages1: stringifyPackages(getSingleVersionPackageOutput(d.Packages1)), + Packages2: stringifyPackages(getSingleVersionPackageOutput(d.Packages2)), + InfoDiff: stringifyPackageDiff(getSingleVersionInfoDiffOutput(d.InfoDiff)), + } + diffOutputs = append(diffOutputs, diffOutput) + } + + strResult := struct { + Image1 string + Image2 string + DiffType string + Diff []StrDiff + }{ + Image1: r.Image1, + Image2: r.Image2, + DiffType: r.DiffType, + Diff: diffOutputs, + } + return TemplateOutputFromFormat(strResult, "SingleVersionPackageLayerDiff", format) +} + type HistDiffResult DiffResult func (r HistDiffResult) OutputStruct() interface{} { diff --git a/util/format_utils.go b/util/format_utils.go index fc263bb8..9bdb2111 100644 --- a/util/format_utils.go +++ b/util/format_utils.go @@ -29,18 +29,20 @@ import ( ) var templates = map[string]string{ - "SingleVersionPackageDiff": SingleVersionDiffOutput, - "MultiVersionPackageDiff": MultiVersionDiffOutput, - "HistDiff": HistoryDiffOutput, - "MetadataDiff": MetadataDiffOutput, - "DirDiff": FSDiffOutput, - "MultipleDirDiff": FSLayerDiffOutput, - "FilenameDiff": FilenameDiffOutput, - "ListAnalyze": ListAnalysisOutput, - "FileAnalyze": FileAnalysisOutput, - "FileLayerAnalyze": FileLayerAnalysisOutput, - "MultiVersionPackageAnalyze": MultiVersionPackageOutput, - "SingleVersionPackageAnalyze": SingleVersionPackageOutput, + "SingleVersionPackageDiff": SingleVersionDiffOutput, + "SingleVersionPackageLayerDiff": SingleVersionLayerDiffOutput, + "MultiVersionPackageDiff": MultiVersionDiffOutput, + "HistDiff": HistoryDiffOutput, + "MetadataDiff": MetadataDiffOutput, + "DirDiff": FSDiffOutput, + "MultipleDirDiff": FSLayerDiffOutput, + "FilenameDiff": FilenameDiffOutput, + "ListAnalyze": ListAnalysisOutput, + "FileAnalyze": FileAnalysisOutput, + "FileLayerAnalyze": FileLayerAnalysisOutput, + "MultiVersionPackageAnalyze": MultiVersionPackageOutput, + "SingleVersionPackageAnalyze": SingleVersionPackageOutput, + "SingleVersionPackageLayerAnalyze": SingleVersionPackageLayerOutput, } func JSONify(diff interface{}) error { diff --git a/util/package_diff_utils.go b/util/package_diff_utils.go index 6afd8639..38eca456 100644 --- a/util/package_diff_utils.go +++ b/util/package_diff_utils.go @@ -46,6 +46,12 @@ type PackageDiff struct { InfoDiff []Info } +// PackageLayerDiff stores the difference information between two images +// layer by layer in PackageDiff array +type PackageLayerDiff struct { + PackageDiffs []PackageDiff +} + // Info stores the information for one package in two different images. type Info struct { Package string diff --git a/util/template_utils.go b/util/template_utils.go index c235e68b..6858b4d5 100644 --- a/util/template_utils.go +++ b/util/template_utils.go @@ -61,6 +61,22 @@ PACKAGE IMAGE1 ({{.Image1}}) IMAGE2 ({{.Image2}}){{range .Diff.InfoDiff}}{{"\n"} {{end}} ` +const SingleVersionLayerDiffOutput = ` +-----{{.DiffType}}----- +{{range $index, $diff := .Diff}} +Diff for Layer {{$index}}: {{if not (or (or $diff.Packages1 $diff.Packages2) $diff.InfoDiff)}} No differences {{else}} +Packages found only in {{$.Image1}}:{{if not $diff.Packages1}} None{{else}} +NAME VERSION SIZE{{range $diff.Packages1}}{{"\n"}}{{print "-"}}{{.Name}} {{.Version}} {{.Size}}{{end}}{{end}} + +Packages found only in {{$.Image2}}:{{if not $diff.Packages2}} None{{else}} +NAME VERSION SIZE{{range $diff.Packages2}}{{"\n"}}{{print "-"}}{{.Name}} {{.Version}} {{.Size}}{{end}}{{end}} + +Version differences:{{if not $diff.InfoDiff}} None{{else}} +PACKAGE IMAGE1 ({{$.Image1}}) IMAGE2 ({{$.Image2}}){{range $diff.InfoDiff}}{{"\n"}}{{print "-"}}{{.Package}} {{.Info1.Version}}, {{.Info1.Size}} {{.Info2.Version}}, {{.Info2.Size}}{{end}} +{{end}}{{end}} +{{end}} +` + const MultiVersionDiffOutput = ` -----{{.DiffType}}----- @@ -139,3 +155,19 @@ Packages found in {{.Image}}:{{if not .Analysis}} None{{else}} NAME VERSION SIZE{{range .Analysis}}{{"\n"}}{{print "-"}}{{.Name}} {{.Version}} {{.Size}}{{end}} {{end}} ` + +const SingleVersionPackageLayerOutput = ` +-----{{.AnalyzeType}}----- +{{range $index, $analysis := .Analysis}} +For Layer {{$index}}:{{if not (or (or $analysis.Packages1 $analysis.Packages2) $analysis.InfoDiff)}} No package changes {{else}} +{{if ne $index 0}}Deleted packages from previous layers:{{if not $analysis.Packages1}} None{{else}} +NAME VERSION SIZE{{range $analysis.Packages1}}{{"\n"}}{{print "-"}}{{.Name}} {{.Version}} {{.Size}}{{end}}{{end}} + +{{end}}Packages added in this layer:{{if not $analysis.Packages2}} None{{else}} +NAME VERSION SIZE{{range $analysis.Packages2}}{{"\n"}}{{print "-"}}{{.Name}} {{.Version}} {{.Size}}{{end}}{{end}} +{{if ne $index 0}} +Version differences:{{if not $analysis.InfoDiff}} None{{else}} +PACKAGE PREV_LAYER CURRENT_LAYER {{range $analysis.InfoDiff}}{{"\n"}}{{print "-"}}{{.Package}} {{.Info1.Version}}, {{.Info1.Size}} {{.Info2.Version}}, {{.Info2.Size}}{{end}} +{{end}}{{end}}{{end}} +{{end}} +` From c1e19885d9fac0707a985e60f75f4ef2bab4dea0 Mon Sep 17 00:00:00 2001 From: David Cassany Date: Wed, 11 Jul 2018 11:02:22 +0200 Subject: [PATCH 2/4] Add aptlayer analyzer This commit implements layer analysis for single version package analyzer for images based on apt package manager --- cmd/root.go | 2 +- differs/apt_diff.go | 63 ++++++++++++++++++++++++++++++++++++++++++++- differs/differs.go | 1 + 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index b10e5030..8b84bbf4 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -268,7 +268,7 @@ func getExtractPathForName(name string) (string, error) { func includeLayers() bool { for _, t := range types { - if t == "layer" { + if strings.Contains(t, "layer") { return true } } diff --git a/differs/apt_diff.go b/differs/apt_diff.go index d4d73bd4..f1a6bb04 100644 --- a/differs/apt_diff.go +++ b/differs/apt_diff.go @@ -28,6 +28,9 @@ import ( "github.com/sirupsen/logrus" ) +//APT package database location +const dpkgStatusFile string = "var/lib/dpkg/status" + type AptAnalyzer struct { } @@ -53,7 +56,7 @@ func (a AptAnalyzer) getPackages(image pkgutil.Image) (map[string]util.PackageIn // invalid image directory path return packages, err } - statusFile := filepath.Join(path, "var/lib/dpkg/status") + statusFile := filepath.Join(path, dpkgStatusFile) if _, err := os.Stat(statusFile); err != nil { // status file does not exist in this layer return packages, nil @@ -120,3 +123,61 @@ func parseLine(text string, currPackage string, packages map[string]util.Package } return currPackage } + +type AptLayerAnalyzer struct { +} + +func (a AptLayerAnalyzer) Name() string { + return "AptLayerAnalyzer" +} + +// AptDiff compares the packages installed by apt-get. +func (a AptLayerAnalyzer) Diff(image1, image2 pkgutil.Image) (util.Result, error) { + diff, err := singleVersionLayerDiff(image1, image2, a) + return diff, err +} + +func (a AptLayerAnalyzer) Analyze(image pkgutil.Image) (util.Result, error) { + analysis, err := singleVersionLayerAnalysis(image, a) + return analysis, err +} + +func (a AptLayerAnalyzer) getPackages(image pkgutil.Image) ([]map[string]util.PackageInfo, error) { + var packages []map[string]util.PackageInfo + if _, err := os.Stat(image.FSPath); err != nil { + // invalid image directory path + return packages, err + } + statusFile := filepath.Join(image.FSPath, dpkgStatusFile) + if _, err := os.Stat(statusFile); err != nil { + // status file does not exist in this image + return packages, nil + } + for _, layer := range image.Layers { + layerPackages := make(map[string]util.PackageInfo) + if _, err := os.Stat(layer.FSPath); err != nil { + // invalid layer directory path + return packages, err + } + statusFile := filepath.Join(layer.FSPath, dpkgStatusFile) + if _, err := os.Stat(statusFile); err == nil { + // this layer has a package database + if file, err := os.Open(statusFile); err == nil { + // make sure it gets closed + defer file.Close() + + // create a new scanner and read the file line by line + scanner := bufio.NewScanner(file) + var currPackage string + for scanner.Scan() { + currPackage = parseLine(scanner.Text(), currPackage, layerPackages) + } + } else { + return packages, err + } + } + packages = append(packages, layerPackages) + } + + return packages, nil +} diff --git a/differs/differs.go b/differs/differs.go index 1658e540..c8c617b2 100644 --- a/differs/differs.go +++ b/differs/differs.go @@ -47,6 +47,7 @@ var Analyzers = map[string]Analyzer{ "file": FileAnalyzer{}, "layer": FileLayerAnalyzer{}, "apt": AptAnalyzer{}, + "aptlayer": AptLayerAnalyzer{}, "rpm": RPMAnalyzer{}, "pip": PipAnalyzer{}, "node": NodeAnalyzer{}, From 21472e8b873bddd2d0f9b12ac5712881a32daa4b Mon Sep 17 00:00:00 2001 From: David Cassany Date: Mon, 16 Jul 2018 18:31:54 +0200 Subject: [PATCH 3/4] Improve coding style * Add a shared method in getPackages for AptAnalyzer and AptLayerAnalyzer * Explicit validation of the type flag in includeLayers method --- cmd/root.go | 8 ++++++-- differs/apt_diff.go | 31 ++++++++----------------------- 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 8b84bbf4..e2a949da 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -59,6 +59,8 @@ const ( RemotePrefix = "remote://" ) +var layerAnalyzers = [...]string{"layer", "aptlayer"} + var RootCmd = &cobra.Command{ Use: "container-diff", Short: "container-diff is a tool for analyzing and comparing container images", @@ -268,8 +270,10 @@ func getExtractPathForName(name string) (string, error) { func includeLayers() bool { for _, t := range types { - if strings.Contains(t, "layer") { - return true + for _, a := range layerAnalyzers { + if t == a { + return true + } } } return false diff --git a/differs/apt_diff.go b/differs/apt_diff.go index f1a6bb04..b1c85168 100644 --- a/differs/apt_diff.go +++ b/differs/apt_diff.go @@ -50,13 +50,16 @@ func (a AptAnalyzer) Analyze(image pkgutil.Image) (util.Result, error) { } func (a AptAnalyzer) getPackages(image pkgutil.Image) (map[string]util.PackageInfo, error) { - path := image.FSPath + return readStatusFile(image.FSPath) +} + +func readStatusFile(root string) (map[string]util.PackageInfo, error) { packages := make(map[string]util.PackageInfo) - if _, err := os.Stat(path); err != nil { + if _, err := os.Stat(root); err != nil { // invalid image directory path return packages, err } - statusFile := filepath.Join(path, dpkgStatusFile) + statusFile := filepath.Join(root, dpkgStatusFile) if _, err := os.Stat(statusFile); err != nil { // status file does not exist in this layer return packages, nil @@ -154,28 +157,10 @@ func (a AptLayerAnalyzer) getPackages(image pkgutil.Image) ([]map[string]util.Pa return packages, nil } for _, layer := range image.Layers { - layerPackages := make(map[string]util.PackageInfo) - if _, err := os.Stat(layer.FSPath); err != nil { - // invalid layer directory path + layerPackages, err := readStatusFile(layer.FSPath) + if err != nil { return packages, err } - statusFile := filepath.Join(layer.FSPath, dpkgStatusFile) - if _, err := os.Stat(statusFile); err == nil { - // this layer has a package database - if file, err := os.Open(statusFile); err == nil { - // make sure it gets closed - defer file.Close() - - // create a new scanner and read the file line by line - scanner := bufio.NewScanner(file) - var currPackage string - for scanner.Scan() { - currPackage = parseLine(scanner.Text(), currPackage, layerPackages) - } - } else { - return packages, err - } - } packages = append(packages, layerPackages) } From 1edcffb97061f2cf3597c8a41611997fe5e720c2 Mon Sep 17 00:00:00 2001 From: David Cassany Date: Mon, 23 Jul 2018 17:14:10 +0200 Subject: [PATCH 4/4] Do not implement diff command for layered analysis of packages Remove Diff implementation of single version packages on layers, print a not supported warning message instead and raise error. --- differs/package_differs.go | 43 +++++++------------------------------- util/format_utils.go | 1 - util/template_utils.go | 16 -------------- 3 files changed, 7 insertions(+), 53 deletions(-) diff --git a/differs/package_differs.go b/differs/package_differs.go index 7809ee16..c152a075 100644 --- a/differs/package_differs.go +++ b/differs/package_differs.go @@ -17,6 +17,7 @@ limitations under the License. package differs import ( + "errors" "strings" pkgutil "github.com/GoogleContainerTools/container-diff/pkg/util" @@ -77,41 +78,11 @@ func singleVersionDiff(image1, image2 pkgutil.Image, differ SingleVersionPackage }, nil } -// singleVersionLayerDiff diffs the packages of each image layer by layer +// singleVersionLayerDiff returns an error as this diff is not supported as +// it is far from obvious to define it in meaningful way func singleVersionLayerDiff(image1, image2 pkgutil.Image, differ SingleVersionPackageLayerAnalyzer) (*util.SingleVersionPackageLayerDiffResult, error) { - pack1, err := differ.getPackages(image1) - if err != nil { - return &util.SingleVersionPackageLayerDiffResult{}, err - } - pack2, err := differ.getPackages(image2) - if err != nil { - return &util.SingleVersionPackageLayerDiffResult{}, err - } - var pkgDiffs []util.PackageDiff - - // Go through each layer for image1 - for i := range pack1 { - if i >= len(pack2) { - // Skip diff when there is no layer to compare with in image2 - continue - } - - pkgDiff := util.GetMapDiff(pack1[i], pack2[i]) - pkgDiffs = append(pkgDiffs, pkgDiff) - } - - if len(image1.Layers) != len(image2.Layers) { - logrus.Infof("%s and %s have different number of layers, please consider using container-diff analyze to view the contents of each image in each layer", image1.Source, image2.Source) - } - - return &util.SingleVersionPackageLayerDiffResult{ - Image1: image1.Source, - Image2: image2.Source, - DiffType: strings.TrimSuffix(differ.Name(), "Analyzer"), - Diff: util.PackageLayerDiff{ - PackageDiffs: pkgDiffs, - }, - }, nil + logrus.Warning("'diff' command for packages on layers is not supported, consider using 'analyze' on each image instead") + return &util.SingleVersionPackageLayerDiffResult{}, errors.New("Diff for packages on layers is not supported, only analysis is supported") } func multiVersionAnalysis(image pkgutil.Image, analyzer MultiVersionPackageAnalyzer) (*util.MultiVersionPackageAnalyzeResult, error) { @@ -153,8 +124,8 @@ func singleVersionLayerAnalysis(image pkgutil.Image, analyzer SingleVersionPacka // Each layer with modified packages includes a complete list of packages // in its package database. Thus we diff the current layer with the - // previous one. Not all layers may include differences in packages, those - // are omitted. + // previous one that contains a package database. Layers that do not + // include a package database are omitted. preInd := -1 for i := range pack { var pkgDiff util.PackageDiff diff --git a/util/format_utils.go b/util/format_utils.go index 9bdb2111..2e93eb6c 100644 --- a/util/format_utils.go +++ b/util/format_utils.go @@ -30,7 +30,6 @@ import ( var templates = map[string]string{ "SingleVersionPackageDiff": SingleVersionDiffOutput, - "SingleVersionPackageLayerDiff": SingleVersionLayerDiffOutput, "MultiVersionPackageDiff": MultiVersionDiffOutput, "HistDiff": HistoryDiffOutput, "MetadataDiff": MetadataDiffOutput, diff --git a/util/template_utils.go b/util/template_utils.go index 6858b4d5..14da15fb 100644 --- a/util/template_utils.go +++ b/util/template_utils.go @@ -61,22 +61,6 @@ PACKAGE IMAGE1 ({{.Image1}}) IMAGE2 ({{.Image2}}){{range .Diff.InfoDiff}}{{"\n"} {{end}} ` -const SingleVersionLayerDiffOutput = ` ------{{.DiffType}}----- -{{range $index, $diff := .Diff}} -Diff for Layer {{$index}}: {{if not (or (or $diff.Packages1 $diff.Packages2) $diff.InfoDiff)}} No differences {{else}} -Packages found only in {{$.Image1}}:{{if not $diff.Packages1}} None{{else}} -NAME VERSION SIZE{{range $diff.Packages1}}{{"\n"}}{{print "-"}}{{.Name}} {{.Version}} {{.Size}}{{end}}{{end}} - -Packages found only in {{$.Image2}}:{{if not $diff.Packages2}} None{{else}} -NAME VERSION SIZE{{range $diff.Packages2}}{{"\n"}}{{print "-"}}{{.Name}} {{.Version}} {{.Size}}{{end}}{{end}} - -Version differences:{{if not $diff.InfoDiff}} None{{else}} -PACKAGE IMAGE1 ({{$.Image1}}) IMAGE2 ({{$.Image2}}){{range $diff.InfoDiff}}{{"\n"}}{{print "-"}}{{.Package}} {{.Info1.Version}}, {{.Info1.Size}} {{.Info2.Version}}, {{.Info2.Size}}{{end}} -{{end}}{{end}} -{{end}} -` - const MultiVersionDiffOutput = ` -----{{.DiffType}}-----