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

Commit 1f50d17

Browse files
committed
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 <[email protected]>
1 parent 0aea57d commit 1f50d17

File tree

6 files changed

+269
-12
lines changed

6 files changed

+269
-12
lines changed

differs/package_differs.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121

2222
pkgutil "github.com/GoogleContainerTools/container-diff/pkg/util"
2323
"github.com/GoogleContainerTools/container-diff/util"
24+
"github.com/sirupsen/logrus"
2425
)
2526

2627
type MultiVersionPackageAnalyzer interface {
@@ -33,6 +34,11 @@ type SingleVersionPackageAnalyzer interface {
3334
Name() string
3435
}
3536

37+
type SingleVersionPackageLayerAnalyzer interface {
38+
getPackages(image pkgutil.Image) ([]map[string]util.PackageInfo, error)
39+
Name() string
40+
}
41+
3642
func multiVersionDiff(image1, image2 pkgutil.Image, differ MultiVersionPackageAnalyzer) (*util.MultiVersionPackageDiffResult, error) {
3743
pack1, err := differ.getPackages(image1)
3844
if err != nil {
@@ -71,6 +77,43 @@ func singleVersionDiff(image1, image2 pkgutil.Image, differ SingleVersionPackage
7177
}, nil
7278
}
7379

80+
// singleVersionLayerDiff diffs the packages of each image layer by layer
81+
func singleVersionLayerDiff(image1, image2 pkgutil.Image, differ SingleVersionPackageLayerAnalyzer) (*util.SingleVersionPackageLayerDiffResult, error) {
82+
pack1, err := differ.getPackages(image1)
83+
if err != nil {
84+
return &util.SingleVersionPackageLayerDiffResult{}, err
85+
}
86+
pack2, err := differ.getPackages(image2)
87+
if err != nil {
88+
return &util.SingleVersionPackageLayerDiffResult{}, err
89+
}
90+
var pkgDiffs []util.PackageDiff
91+
92+
// Go through each layer for image1
93+
for i := range pack1 {
94+
if i >= len(pack2) {
95+
// Skip diff when there is no layer to compare with in image2
96+
continue
97+
}
98+
99+
pkgDiff := util.GetMapDiff(pack1[i], pack2[i])
100+
pkgDiffs = append(pkgDiffs, pkgDiff)
101+
}
102+
103+
if len(image1.Layers) != len(image2.Layers) {
104+
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)
105+
}
106+
107+
return &util.SingleVersionPackageLayerDiffResult{
108+
Image1: image1.Source,
109+
Image2: image2.Source,
110+
DiffType: strings.TrimSuffix(differ.Name(), "Analyzer"),
111+
Diff: util.PackageLayerDiff{
112+
PackageDiffs: pkgDiffs,
113+
},
114+
}, nil
115+
}
116+
74117
func multiVersionAnalysis(image pkgutil.Image, analyzer MultiVersionPackageAnalyzer) (*util.MultiVersionPackageAnalyzeResult, error) {
75118
pack, err := analyzer.getPackages(image)
76119
if err != nil {
@@ -98,3 +141,39 @@ func singleVersionAnalysis(image pkgutil.Image, analyzer SingleVersionPackageAna
98141
}
99142
return &analysis, nil
100143
}
144+
145+
// singleVersionLayerAnalysis returns the packages included, deleted or
146+
// updated in each layer
147+
func singleVersionLayerAnalysis(image pkgutil.Image, analyzer SingleVersionPackageLayerAnalyzer) (*util.SingleVersionPackageLayerAnalyzeResult, error) {
148+
pack, err := analyzer.getPackages(image)
149+
if err != nil {
150+
return &util.SingleVersionPackageLayerAnalyzeResult{}, err
151+
}
152+
var pkgDiffs []util.PackageDiff
153+
154+
// Each layer with modified packages includes a complete list of packages
155+
// in its package database. Thus we diff the current layer with the
156+
// previous one. Not all layers may include differences in packages, those
157+
// are omitted.
158+
preInd := -1
159+
for i := range pack {
160+
var pkgDiff util.PackageDiff
161+
if preInd < 0 && len(pack[i]) > 0 {
162+
pkgDiff = util.GetMapDiff(make(map[string]util.PackageInfo), pack[i])
163+
preInd = i
164+
} else if preInd >= 0 && len(pack[i]) > 0 {
165+
pkgDiff = util.GetMapDiff(pack[preInd], pack[i])
166+
preInd = i
167+
}
168+
169+
pkgDiffs = append(pkgDiffs, pkgDiff)
170+
}
171+
172+
return &util.SingleVersionPackageLayerAnalyzeResult{
173+
Image: image.Source,
174+
AnalyzeType: strings.TrimSuffix(analyzer.Name(), "Analyzer"),
175+
Analysis: util.PackageLayerDiff{
176+
PackageDiffs: pkgDiffs,
177+
},
178+
}, nil
179+
}

util/analyze_output_utils.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,78 @@ func (r SingleVersionPackageAnalyzeResult) OutputText(diffType string, format st
136136
return TemplateOutputFromFormat(strResult, "SingleVersionPackageAnalyze", format)
137137
}
138138

139+
type SingleVersionPackageLayerAnalyzeResult AnalyzeResult
140+
141+
func (r SingleVersionPackageLayerAnalyzeResult) OutputStruct() interface{} {
142+
analysis, valid := r.Analysis.(PackageLayerDiff)
143+
if !valid {
144+
logrus.Error("Unexpected structure of Analysis. Should be of type PackageLayerDiff")
145+
return fmt.Errorf("Could not output %s analysis result", r.AnalyzeType)
146+
}
147+
148+
type PkgDiff struct {
149+
Packages1 []PackageOutput
150+
Packages2 []PackageOutput
151+
InfoDiff []Info
152+
}
153+
154+
var analysisOutput []PkgDiff
155+
for _, d := range analysis.PackageDiffs {
156+
diffOutput := PkgDiff{
157+
Packages1: getSingleVersionPackageOutput(d.Packages1),
158+
Packages2: getSingleVersionPackageOutput(d.Packages2),
159+
InfoDiff: getSingleVersionInfoDiffOutput(d.InfoDiff),
160+
}
161+
analysisOutput = append(analysisOutput, diffOutput)
162+
}
163+
164+
output := struct {
165+
Image string
166+
AnalyzeType string
167+
Analysis []PkgDiff
168+
}{
169+
Image: r.Image,
170+
AnalyzeType: r.AnalyzeType,
171+
Analysis: analysisOutput,
172+
}
173+
return output
174+
}
175+
176+
func (r SingleVersionPackageLayerAnalyzeResult) OutputText(diffType string, format string) error {
177+
analysis, valid := r.Analysis.(PackageLayerDiff)
178+
if !valid {
179+
logrus.Error("Unexpected structure of Analysis. Should be of type PackageLayerDiff")
180+
return fmt.Errorf("Could not output %s analysis result", r.AnalyzeType)
181+
}
182+
183+
type StrDiff struct {
184+
Packages1 []StrPackageOutput
185+
Packages2 []StrPackageOutput
186+
InfoDiff []StrInfo
187+
}
188+
189+
var analysisOutput []StrDiff
190+
for _, d := range analysis.PackageDiffs {
191+
diffOutput := StrDiff{
192+
Packages1: stringifyPackages(getSingleVersionPackageOutput(d.Packages1)),
193+
Packages2: stringifyPackages(getSingleVersionPackageOutput(d.Packages2)),
194+
InfoDiff: stringifyPackageDiff(getSingleVersionInfoDiffOutput(d.InfoDiff)),
195+
}
196+
analysisOutput = append(analysisOutput, diffOutput)
197+
}
198+
199+
strResult := struct {
200+
Image string
201+
AnalyzeType string
202+
Analysis []StrDiff
203+
}{
204+
Image: r.Image,
205+
AnalyzeType: r.AnalyzeType,
206+
Analysis: analysisOutput,
207+
}
208+
return TemplateOutputFromFormat(strResult, "SingleVersionPackageLayerAnalyze", format)
209+
}
210+
139211
type PackageOutput struct {
140212
Name string
141213
Path string `json:",omitempty"`

util/diff_output_utils.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,72 @@ func getSingleVersionInfoDiffOutput(infoDiff []Info) []Info {
162162
return infoDiff
163163
}
164164

165+
type SingleVersionPackageLayerDiffResult DiffResult
166+
167+
func (r SingleVersionPackageLayerDiffResult) OutputStruct() interface{} {
168+
diff, valid := r.Diff.(PackageLayerDiff)
169+
if !valid {
170+
logrus.Error("Unexpected structure of Diff. Should follow the PackageLayerDiff struct")
171+
return fmt.Errorf("Could not output %s diff result", r.DiffType)
172+
}
173+
174+
type PkgDiff struct {
175+
Packages1 []PackageOutput
176+
Packages2 []PackageOutput
177+
InfoDiff []Info
178+
}
179+
180+
var diffOutputs []PkgDiff
181+
for _, d := range diff.PackageDiffs {
182+
diffOutput := PkgDiff{
183+
Packages1: getSingleVersionPackageOutput(d.Packages1),
184+
Packages2: getSingleVersionPackageOutput(d.Packages2),
185+
InfoDiff: getSingleVersionInfoDiffOutput(d.InfoDiff),
186+
}
187+
diffOutputs = append(diffOutputs, diffOutput)
188+
}
189+
190+
r.Diff = diffOutputs
191+
return r
192+
}
193+
194+
func (r SingleVersionPackageLayerDiffResult) OutputText(diffType string, format string) error {
195+
diff, valid := r.Diff.(PackageLayerDiff)
196+
if !valid {
197+
logrus.Error("Unexpected structure of Diff. Should follow the PackageLayerDiff struct")
198+
return fmt.Errorf("Could not output %s diff result", r.DiffType)
199+
}
200+
201+
type StrDiff struct {
202+
Packages1 []StrPackageOutput
203+
Packages2 []StrPackageOutput
204+
InfoDiff []StrInfo
205+
}
206+
207+
var diffOutputs []StrDiff
208+
for _, d := range diff.PackageDiffs {
209+
diffOutput := StrDiff{
210+
Packages1: stringifyPackages(getSingleVersionPackageOutput(d.Packages1)),
211+
Packages2: stringifyPackages(getSingleVersionPackageOutput(d.Packages2)),
212+
InfoDiff: stringifyPackageDiff(getSingleVersionInfoDiffOutput(d.InfoDiff)),
213+
}
214+
diffOutputs = append(diffOutputs, diffOutput)
215+
}
216+
217+
strResult := struct {
218+
Image1 string
219+
Image2 string
220+
DiffType string
221+
Diff []StrDiff
222+
}{
223+
Image1: r.Image1,
224+
Image2: r.Image2,
225+
DiffType: r.DiffType,
226+
Diff: diffOutputs,
227+
}
228+
return TemplateOutputFromFormat(strResult, "SingleVersionPackageLayerDiff", format)
229+
}
230+
165231
type HistDiffResult DiffResult
166232

167233
func (r HistDiffResult) OutputStruct() interface{} {

util/format_utils.go

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,20 @@ import (
2929
)
3030

3131
var templates = map[string]string{
32-
"SingleVersionPackageDiff": SingleVersionDiffOutput,
33-
"MultiVersionPackageDiff": MultiVersionDiffOutput,
34-
"HistDiff": HistoryDiffOutput,
35-
"MetadataDiff": MetadataDiffOutput,
36-
"DirDiff": FSDiffOutput,
37-
"MultipleDirDiff": FSLayerDiffOutput,
38-
"FilenameDiff": FilenameDiffOutput,
39-
"ListAnalyze": ListAnalysisOutput,
40-
"FileAnalyze": FileAnalysisOutput,
41-
"FileLayerAnalyze": FileLayerAnalysisOutput,
42-
"MultiVersionPackageAnalyze": MultiVersionPackageOutput,
43-
"SingleVersionPackageAnalyze": SingleVersionPackageOutput,
32+
"SingleVersionPackageDiff": SingleVersionDiffOutput,
33+
"SingleVersionPackageLayerDiff": SingleVersionLayerDiffOutput,
34+
"MultiVersionPackageDiff": MultiVersionDiffOutput,
35+
"HistDiff": HistoryDiffOutput,
36+
"MetadataDiff": MetadataDiffOutput,
37+
"DirDiff": FSDiffOutput,
38+
"MultipleDirDiff": FSLayerDiffOutput,
39+
"FilenameDiff": FilenameDiffOutput,
40+
"ListAnalyze": ListAnalysisOutput,
41+
"FileAnalyze": FileAnalysisOutput,
42+
"FileLayerAnalyze": FileLayerAnalysisOutput,
43+
"MultiVersionPackageAnalyze": MultiVersionPackageOutput,
44+
"SingleVersionPackageAnalyze": SingleVersionPackageOutput,
45+
"SingleVersionPackageLayerAnalyze": SingleVersionPackageLayerOutput,
4446
}
4547

4648
func JSONify(diff interface{}) error {

util/package_diff_utils.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ type PackageDiff struct {
4646
InfoDiff []Info
4747
}
4848

49+
// PackageLayerDiff stores the difference information between two images
50+
// layer by layer in PackageDiff array
51+
type PackageLayerDiff struct {
52+
PackageDiffs []PackageDiff
53+
}
54+
4955
// Info stores the information for one package in two different images.
5056
type Info struct {
5157
Package string

util/template_utils.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,22 @@ PACKAGE IMAGE1 ({{.Image1}}) IMAGE2 ({{.Image2}}){{range .Diff.InfoDiff}}{{"\n"}
6161
{{end}}
6262
`
6363

64+
const SingleVersionLayerDiffOutput = `
65+
-----{{.DiffType}}-----
66+
{{range $index, $diff := .Diff}}
67+
Diff for Layer {{$index}}: {{if not (or (or $diff.Packages1 $diff.Packages2) $diff.InfoDiff)}} No differences {{else}}
68+
Packages found only in {{$.Image1}}:{{if not $diff.Packages1}} None{{else}}
69+
NAME VERSION SIZE{{range $diff.Packages1}}{{"\n"}}{{print "-"}}{{.Name}} {{.Version}} {{.Size}}{{end}}{{end}}
70+
71+
Packages found only in {{$.Image2}}:{{if not $diff.Packages2}} None{{else}}
72+
NAME VERSION SIZE{{range $diff.Packages2}}{{"\n"}}{{print "-"}}{{.Name}} {{.Version}} {{.Size}}{{end}}{{end}}
73+
74+
Version differences:{{if not $diff.InfoDiff}} None{{else}}
75+
PACKAGE IMAGE1 ({{$.Image1}}) IMAGE2 ({{$.Image2}}){{range $diff.InfoDiff}}{{"\n"}}{{print "-"}}{{.Package}} {{.Info1.Version}}, {{.Info1.Size}} {{.Info2.Version}}, {{.Info2.Size}}{{end}}
76+
{{end}}{{end}}
77+
{{end}}
78+
`
79+
6480
const MultiVersionDiffOutput = `
6581
-----{{.DiffType}}-----
6682
@@ -139,3 +155,19 @@ Packages found in {{.Image}}:{{if not .Analysis}} None{{else}}
139155
NAME VERSION SIZE{{range .Analysis}}{{"\n"}}{{print "-"}}{{.Name}} {{.Version}} {{.Size}}{{end}}
140156
{{end}}
141157
`
158+
159+
const SingleVersionPackageLayerOutput = `
160+
-----{{.AnalyzeType}}-----
161+
{{range $index, $analysis := .Analysis}}
162+
For Layer {{$index}}:{{if not (or (or $analysis.Packages1 $analysis.Packages2) $analysis.InfoDiff)}} No package changes {{else}}
163+
{{if ne $index 0}}Deleted packages from previous layers:{{if not $analysis.Packages1}} None{{else}}
164+
NAME VERSION SIZE{{range $analysis.Packages1}}{{"\n"}}{{print "-"}}{{.Name}} {{.Version}} {{.Size}}{{end}}{{end}}
165+
166+
{{end}}Packages added in this layer:{{if not $analysis.Packages2}} None{{else}}
167+
NAME VERSION SIZE{{range $analysis.Packages2}}{{"\n"}}{{print "-"}}{{.Name}} {{.Version}} {{.Size}}{{end}}{{end}}
168+
{{if ne $index 0}}
169+
Version differences:{{if not $analysis.InfoDiff}} None{{else}}
170+
PACKAGE PREV_LAYER CURRENT_LAYER {{range $analysis.InfoDiff}}{{"\n"}}{{print "-"}}{{.Package}} {{.Info1.Version}}, {{.Info1.Size}} {{.Info2.Version}}, {{.Info2.Size}}{{end}}
171+
{{end}}{{end}}{{end}}
172+
{{end}}
173+
`

0 commit comments

Comments
 (0)