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

Commit 4ab93c8

Browse files
committed
Add image and layer size analyzer
1 parent 78cb059 commit 4ab93c8

11 files changed

+324
-11
lines changed

differs/differs.go

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ const historyAnalyzer = "history"
2828
const metadataAnalyzer = "metadata"
2929
const fileAnalyzer = "file"
3030
const layerAnalyzer = "layer"
31+
const sizeAnalyzer = "size"
32+
const sizeLayerAnalyzer = "sizelayer"
3133
const aptAnalyzer = "apt"
3234
const aptLayerAnalyzer = "aptlayer"
3335
const rpmAnalyzer = "rpm"
@@ -53,19 +55,21 @@ type Analyzer interface {
5355
}
5456

5557
var Analyzers = map[string]Analyzer{
56-
historyAnalyzer: HistoryAnalyzer{},
57-
metadataAnalyzer: MetadataAnalyzer{},
58-
fileAnalyzer: FileAnalyzer{},
59-
layerAnalyzer: FileLayerAnalyzer{},
60-
aptAnalyzer: AptAnalyzer{},
61-
aptLayerAnalyzer: AptLayerAnalyzer{},
62-
rpmAnalyzer: RPMAnalyzer{},
63-
rpmLayerAnalyzer: RPMLayerAnalyzer{},
64-
pipAnalyzer: PipAnalyzer{},
65-
nodeAnalyzer: NodeAnalyzer{},
58+
historyAnalyzer: HistoryAnalyzer{},
59+
metadataAnalyzer: MetadataAnalyzer{},
60+
fileAnalyzer: FileAnalyzer{},
61+
layerAnalyzer: FileLayerAnalyzer{},
62+
sizeAnalyzer: SizeAnalyzer{},
63+
sizeLayerAnalyzer: SizeLayerAnalyzer{},
64+
aptAnalyzer: AptAnalyzer{},
65+
aptLayerAnalyzer: AptLayerAnalyzer{},
66+
rpmAnalyzer: RPMAnalyzer{},
67+
rpmLayerAnalyzer: RPMLayerAnalyzer{},
68+
pipAnalyzer: PipAnalyzer{},
69+
nodeAnalyzer: NodeAnalyzer{},
6670
}
6771

68-
var LayerAnalyzers = [...]string{layerAnalyzer, aptLayerAnalyzer, rpmLayerAnalyzer}
72+
var LayerAnalyzers = [...]string{layerAnalyzer, sizeLayerAnalyzer, aptLayerAnalyzer, rpmLayerAnalyzer}
6973

7074
func (req DiffRequest) GetDiff() (map[string]util.Result, error) {
7175
img1 := req.Image1

differs/size_diff.go

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
Copyright 2018 Google, Inc. All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package differs
18+
19+
import (
20+
"strconv"
21+
22+
pkgutil "github.com/GoogleContainerTools/container-diff/pkg/util"
23+
"github.com/GoogleContainerTools/container-diff/util"
24+
)
25+
26+
type SizeAnalyzer struct {
27+
}
28+
29+
func (a SizeAnalyzer) Name() string {
30+
return "SizeAnalyzer"
31+
}
32+
33+
// SizeDiff diffs two images and compares their size
34+
func (a SizeAnalyzer) Diff(image1, image2 pkgutil.Image) (util.Result, error) {
35+
diff := []util.EntryDiff{
36+
{
37+
Name: "*",
38+
Size1: pkgutil.GetSize(image1.FSPath),
39+
Size2: pkgutil.GetSize(image2.FSPath),
40+
},
41+
}
42+
43+
return &util.SizeDiffResult{
44+
Image1: image1.Source,
45+
Image2: image2.Source,
46+
DiffType: "Size",
47+
Diff: diff,
48+
}, nil
49+
}
50+
51+
func (a SizeAnalyzer) Analyze(image pkgutil.Image) (util.Result, error) {
52+
entries := []pkgutil.DirectoryEntry{
53+
{
54+
Name: "*",
55+
Size: pkgutil.GetSize(image.FSPath),
56+
},
57+
}
58+
59+
return &util.SizeAnalyzeResult{
60+
Image: image.Source,
61+
AnalyzeType: "Size",
62+
Analysis: entries,
63+
}, nil
64+
}
65+
66+
type SizeLayerAnalyzer struct {
67+
}
68+
69+
func (a SizeLayerAnalyzer) Name() string {
70+
return "SizeLayerAnalyzer"
71+
}
72+
73+
// SizeLayerDiff diffs the layers of two images and compares their size
74+
func (a SizeLayerAnalyzer) Diff(image1, image2 pkgutil.Image) (util.Result, error) {
75+
var layerDiffs []util.EntryDiff
76+
77+
maxLayer := len(image1.Layers)
78+
if len(image2.Layers) > maxLayer {
79+
maxLayer = len(image2.Layers)
80+
}
81+
82+
for index := 0; index < maxLayer; index++ {
83+
var size1, size2 int64 = -1, -1
84+
if index < len(image1.Layers) {
85+
size1 = pkgutil.GetSize(image1.Layers[index].FSPath)
86+
}
87+
if index < len(image2.Layers) {
88+
size2 = pkgutil.GetSize(image2.Layers[index].FSPath)
89+
}
90+
91+
diff := util.EntryDiff{
92+
Name: strconv.Itoa(index),
93+
Size1: size1,
94+
Size2: size2,
95+
}
96+
layerDiffs = append(layerDiffs, diff)
97+
}
98+
99+
return &util.SizeDiffResult{
100+
Image1: image1.Source,
101+
Image2: image2.Source,
102+
DiffType: "SizeLayer",
103+
Diff: layerDiffs,
104+
}, nil
105+
}
106+
107+
func (a SizeLayerAnalyzer) Analyze(image pkgutil.Image) (util.Result, error) {
108+
var entries []pkgutil.DirectoryEntry
109+
for index, layer := range image.Layers {
110+
entry := pkgutil.DirectoryEntry{
111+
Name: strconv.Itoa(index),
112+
Size: pkgutil.GetSize(layer.FSPath),
113+
}
114+
entries = append(entries, entry)
115+
}
116+
117+
return &util.SizeAnalyzeResult{
118+
Image: image.Source,
119+
AnalyzeType: "SizeLayer",
120+
Analysis: entries,
121+
}, nil
122+
}

tests/integration_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,22 @@ func TestDiffAndAnalysis(t *testing.T) {
117117
differFlags: []string{"--type=layer", "--no-cache"},
118118
expectedFile: "file_layer_diff_expected.json",
119119
},
120+
{
121+
description: "size differ",
122+
subcommand: "diff",
123+
imageA: diffLayerBase,
124+
imageB: diffLayerModifed,
125+
differFlags: []string{"--type=size", "--no-cache"},
126+
expectedFile: "size_diff_expected.json",
127+
},
128+
{
129+
description: "size layer differ",
130+
subcommand: "diff",
131+
imageA: diffLayerBase,
132+
imageB: diffLayerModifed,
133+
differFlags: []string{"--type=sizelayer", "--no-cache"},
134+
expectedFile: "size_layer_diff_expected.json",
135+
},
120136
{
121137
description: "apt differ",
122138
subcommand: "diff",
@@ -209,6 +225,20 @@ func TestDiffAndAnalysis(t *testing.T) {
209225
differFlags: []string{"--type=layer", "--no-cache"},
210226
expectedFile: "file_layer_analysis_expected.json",
211227
},
228+
{
229+
description: "size analysis",
230+
subcommand: "analyze",
231+
imageA: diffBase,
232+
differFlags: []string{"--type=size", "--no-cache"},
233+
expectedFile: "size_analysis_expected.json",
234+
},
235+
{
236+
description: "size layer analysis",
237+
subcommand: "analyze",
238+
imageA: diffLayerBase,
239+
differFlags: []string{"--type=sizelayer", "--no-cache"},
240+
expectedFile: "size_layer_analysis_expected.json",
241+
},
212242
{
213243
description: "pip analysis",
214244
subcommand: "analyze",

tests/size_analysis_expected.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[
2+
{
3+
"Image": "gcr.io/gcp-runtimes/diff-base",
4+
"AnalyzeType": "Size",
5+
"Analysis": [
6+
{
7+
"Name": "*",
8+
"Size": 383043168
9+
}
10+
]
11+
}
12+
]

tests/size_diff_expected.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[
2+
{
3+
"Image1": "gcr.io/gcp-runtimes/diff-layer-base",
4+
"Image2": "gcr.io/gcp-runtimes/diff-layer-modified",
5+
"DiffType": "Size",
6+
"Diff": [
7+
{
8+
"Name": "*",
9+
"Size1": 19,
10+
"Size2": 15
11+
}
12+
]
13+
}
14+
]
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[
2+
{
3+
"Image": "gcr.io/gcp-runtimes/diff-layer-base",
4+
"AnalyzeType": "SizeLayer",
5+
"Analysis": [
6+
{
7+
"Name": "0",
8+
"Size": 6
9+
},
10+
{
11+
"Name": "1",
12+
"Size": 7
13+
},
14+
{
15+
"Name": "2",
16+
"Size": 6
17+
}
18+
]
19+
}
20+
]

tests/size_layer_diff_expected.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[
2+
{
3+
"Image1": "gcr.io/gcp-runtimes/diff-layer-base",
4+
"Image2": "gcr.io/gcp-runtimes/diff-layer-modified",
5+
"DiffType": "SizeLayer",
6+
"Diff": [
7+
{
8+
"Name": "0",
9+
"Size1": 6,
10+
"Size2": 6
11+
},
12+
{
13+
"Name": "1",
14+
"Size1": 7,
15+
"Size2": 9
16+
},
17+
{
18+
"Name": "2",
19+
"Size1": 6,
20+
"Size2": -1
21+
}
22+
]
23+
}
24+
]

util/analyze_output_utils.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,3 +340,36 @@ func (r FileLayerAnalyzeResult) OutputText(analyzeType string, format string) er
340340
}
341341
return TemplateOutputFromFormat(strResult, "FileLayerAnalyze", format)
342342
}
343+
344+
type SizeAnalyzeResult AnalyzeResult
345+
346+
func (r SizeAnalyzeResult) OutputStruct() interface{} {
347+
analysis, valid := r.Analysis.([]util.DirectoryEntry)
348+
if !valid {
349+
logrus.Error("Unexpected structure of Analysis. Should be of type []DirectoryEntry")
350+
return errors.New("Could not output SizeAnalyzer analysis result")
351+
}
352+
r.Analysis = analysis
353+
return r
354+
}
355+
356+
func (r SizeAnalyzeResult) OutputText(analyzeType string, format string) error {
357+
analysis, valid := r.Analysis.([]util.DirectoryEntry)
358+
if !valid {
359+
logrus.Error("Unexpected structure of Analysis. Should be of type []DirectoryEntry")
360+
return errors.New("Could not output SizeAnalyzer analysis result")
361+
}
362+
363+
strAnalysis := stringifyDirectoryEntries(analysis)
364+
365+
strResult := struct {
366+
Image string
367+
AnalyzeType string
368+
Analysis []StrDirectoryEntry
369+
}{
370+
Image: r.Image,
371+
AnalyzeType: r.AnalyzeType,
372+
Analysis: strAnalysis,
373+
}
374+
return TemplateOutputFromFormat(strResult, "SizeAnalyze", format)
375+
}

util/diff_output_utils.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,42 @@ func (r DirDiffResult) OutputText(diffType string, format string) error {
297297
return TemplateOutputFromFormat(strResult, "DirDiff", format)
298298
}
299299

300+
type SizeDiffResult DiffResult
301+
302+
func (r SizeDiffResult) OutputStruct() interface{} {
303+
diff, valid := r.Diff.([]EntryDiff)
304+
if !valid {
305+
logrus.Error("Unexpected structure of Diff. Should be of type []EntryDiff")
306+
return errors.New("Could not output SizeAnalyzer diff result")
307+
}
308+
309+
r.Diff = diff
310+
return r
311+
}
312+
313+
func (r SizeDiffResult) OutputText(diffType string, format string) error {
314+
diff, valid := r.Diff.([]EntryDiff)
315+
if !valid {
316+
logrus.Error("Unexpected structure of Diff. Should be of type []EntryDiff")
317+
return errors.New("Could not output SizeAnalyzer diff result")
318+
}
319+
320+
strDiff := stringifyEntryDiffs(diff)
321+
322+
strResult := struct {
323+
Image1 string
324+
Image2 string
325+
DiffType string
326+
Diff []StrEntryDiff
327+
}{
328+
Image1: r.Image1,
329+
Image2: r.Image2,
330+
DiffType: r.DiffType,
331+
Diff: strDiff,
332+
}
333+
return TemplateOutputFromFormat(strResult, "SizeDiff", format)
334+
}
335+
300336
type MultipleDirDiffResult DiffResult
301337

302338
func (r MultipleDirDiffResult) OutputStruct() interface{} {

util/format_utils.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ var templates = map[string]string{
3939
"ListAnalyze": ListAnalysisOutput,
4040
"FileAnalyze": FileAnalysisOutput,
4141
"FileLayerAnalyze": FileLayerAnalysisOutput,
42+
"SizeAnalyze": SizeAnalysisOutput,
43+
"SizeDiff": SizeDiffOutput,
4244
"MultiVersionPackageAnalyze": MultiVersionPackageOutput,
4345
"SingleVersionPackageAnalyze": SingleVersionPackageOutput,
4446
"SingleVersionPackageLayerAnalyze": SingleVersionPackageLayerOutput,

0 commit comments

Comments
 (0)