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

Commit 38f78ae

Browse files
authored
Merge pull request #18 from cftorres/FinalFSFix
Fixed images sourced from tars and IDs to have final filesystem
2 parents caee6e4 + 4a87618 commit 38f78ae

20 files changed

+204
-291
lines changed

differs/fileDiff.go

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
package differs
22

33
import (
4-
"os"
54
"sort"
65

76
"github.com/GoogleCloudPlatform/container-diff/utils"
8-
"github.com/golang/glog"
97
)
108

119
type FileDiffer struct {
@@ -23,21 +21,11 @@ func diffImageFiles(image1, image2 utils.Image) (utils.DirDiff, error) {
2321

2422
var diff utils.DirDiff
2523

26-
target1 := "j1.json"
27-
err := utils.DirToJSON(img1, target1, true)
24+
img1Dir, err := utils.GetDirectory(img1, true)
2825
if err != nil {
2926
return diff, err
3027
}
31-
target2 := "j2.json"
32-
err = utils.DirToJSON(img2, target2, true)
33-
if err != nil {
34-
return diff, err
35-
}
36-
img1Dir, err := utils.GetDirectory(target1)
37-
if err != nil {
38-
return diff, err
39-
}
40-
img2Dir, err := utils.GetDirectory(target2)
28+
img2Dir, err := utils.GetDirectory(img2, true)
4129
if err != nil {
4230
return diff, err
4331
}
@@ -52,15 +40,7 @@ func diffImageFiles(image1, image2 utils.Image) (utils.DirDiff, error) {
5240
Image2: image2.Source,
5341
Adds: adds,
5442
Dels: dels,
55-
}
56-
57-
err = os.Remove(target1)
58-
if err != nil {
59-
glog.Error(err)
60-
}
61-
err = os.Remove(target2)
62-
if err != nil {
63-
glog.Error(err)
43+
Mods: []string{},
6444
}
6545
return diff, nil
6646
}

differs/pipDiff.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ func (d PipDiffer) Diff(image1, image2 utils.Image) (utils.DiffResult, error) {
2121
return diff, err
2222
}
2323

24-
func getPythonVersion(pathToLayer string) ([]string, error) {
24+
func getPythonVersion(pathToImage string) ([]string, error) {
2525
matches := []string{}
26-
libPath := filepath.Join(pathToLayer, "usr/local/lib")
26+
libPath := filepath.Join(pathToImage, "usr/local/lib")
2727
libContents, err := ioutil.ReadDir(libPath)
2828
if err != nil {
2929
return matches, err
@@ -59,9 +59,7 @@ func (d PipDiffer) getPackages(image utils.Image) (map[string]map[string]utils.P
5959
pythonPaths := []string{}
6060
if !reflect.DeepEqual(utils.ConfigSchema{}, image.Config) {
6161
paths := getPythonPaths(image.Config.Config.Env)
62-
for _, p := range paths {
63-
pythonPaths = append(pythonPaths, p)
64-
}
62+
pythonPaths = append(pythonPaths, paths...)
6563
}
6664
pythonVersions, err := getPythonVersion(path)
6765
if err != nil {
@@ -77,7 +75,7 @@ func (d PipDiffer) getPackages(image utils.Image) (map[string]map[string]utils.P
7775
for _, pythonPath := range pythonPaths {
7876
contents, err := ioutil.ReadDir(pythonPath)
7977
if err != nil {
80-
// python version folder doesn't have a site-packages folder
78+
// python version folder doesn't have a site-packages folder or PYTHONPATH doesn't exist
8179
continue
8280
}
8381

tests/file_diff_expected.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"Dels": [
99
"/home/test"
1010
],
11-
"Mods": null
11+
"Mods": []
1212
}
1313
}
1414
]

tests/multi_diff_expected.json

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -101,21 +101,6 @@
101101
}
102102
},
103103
"InfoDiff": [
104-
{
105-
"Package": "wheel",
106-
"Info1": [
107-
{
108-
"Version": "0.29.0",
109-
"Size": "103509"
110-
}
111-
],
112-
"Info2": [
113-
{
114-
"Version": "0.29.0",
115-
"Size": "137451"
116-
}
117-
]
118-
},
119104
{
120105
"Package": "mock",
121106
"Info1": [
@@ -145,6 +130,21 @@
145130
"Size": "1157078"
146131
}
147132
]
133+
},
134+
{
135+
"Package": "wheel",
136+
"Info1": [
137+
{
138+
"Version": "0.29.0",
139+
"Size": "103509"
140+
}
141+
],
142+
"Info2": [
143+
{
144+
"Version": "0.29.0",
145+
"Size": "137451"
146+
}
147+
]
148148
}
149149
]
150150
}

utils/docker_utils.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@ import (
55
"bytes"
66
"context"
77
"encoding/json"
8+
"errors"
89
"fmt"
910
"io"
11+
"io/ioutil"
12+
"os"
1013
"os/exec"
14+
"path/filepath"
1115
"regexp"
1216
"strings"
1317
"syscall"
@@ -36,6 +40,7 @@ func ValidDockerVersion() (bool, error) {
3640
}
3741
return false, nil
3842
}
43+
3944
func getImagePullResponse(image string, response []Event) (string, error) {
4045
var imageDigest string
4146
for _, event := range response {
@@ -303,3 +308,59 @@ func getImageConfig(image string) (container.Config, error) {
303308
}
304309
return config, nil
305310
}
311+
312+
func getLayersFromManifest(manifestPath string) ([]string, error) {
313+
type Manifest struct {
314+
Layers []string
315+
}
316+
317+
manifestJSON, err := ioutil.ReadFile(manifestPath)
318+
if err != nil {
319+
errMsg := fmt.Sprintf("Could not open manifest to get layer order: %s", err)
320+
return []string{}, errors.New(errMsg)
321+
}
322+
323+
var imageManifest []Manifest
324+
err = json.Unmarshal(manifestJSON, &imageManifest)
325+
if err != nil {
326+
errMsg := fmt.Sprintf("Could not unmarshal manifest to get layer order: %s", err)
327+
return []string{}, errors.New(errMsg)
328+
}
329+
return imageManifest[0].Layers, nil
330+
}
331+
332+
func unpackDockerSave(tarPath string, target string) error {
333+
if _, ok := os.Stat(target); ok != nil {
334+
os.MkdirAll(target, 0777)
335+
}
336+
337+
tempLayerDir := target + "-temp"
338+
err := UnTar(tarPath, tempLayerDir)
339+
if err != nil {
340+
errMsg := fmt.Sprintf("Could not unpack saved Docker image %s: %s", tarPath, err)
341+
return errors.New(errMsg)
342+
}
343+
344+
manifest := filepath.Join(tempLayerDir, "manifest.json")
345+
layers, err := getLayersFromManifest(manifest)
346+
if err != nil {
347+
return err
348+
}
349+
350+
for _, layer := range layers {
351+
layerTar := filepath.Join(tempLayerDir, layer)
352+
if _, err := os.Stat(layerTar); err != nil {
353+
glog.Infof("Did not unpack layer %s because no layer.tar found", layer)
354+
continue
355+
}
356+
err = UnTar(layerTar, target)
357+
if err != nil {
358+
glog.Errorf("Could not unpack layer %s: %s", layer, err)
359+
}
360+
}
361+
err = os.RemoveAll(tempLayerDir)
362+
if err != nil {
363+
glog.Errorf("Error deleting temp image layer filesystem: %s", err)
364+
}
365+
return nil
366+
}

utils/fs_utils.go

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ package utils
22

33
import (
44
"bytes"
5-
"encoding/json"
65
"fmt"
76
"io/ioutil"
87
"os"
98
"path/filepath"
9+
"strings"
1010

1111
"github.com/golang/glog"
1212
)
@@ -22,18 +22,33 @@ func GetDirectorySize(path string) (int64, error) {
2222
return size, err
2323
}
2424

25-
func GetDirectory(dirpath string) (Directory, error) {
26-
dirfile, err := ioutil.ReadFile(dirpath)
27-
if err != nil {
28-
return Directory{}, err
29-
}
25+
// GetDirectoryContents converts the directory starting at the provided path into a Directory struct.
26+
func GetDirectory(path string, deep bool) (Directory, error) {
27+
var directory Directory
28+
directory.Root = path
29+
var err error
30+
if deep {
31+
walkFn := func(currPath string, info os.FileInfo, err error) error {
32+
newContent := strings.TrimPrefix(currPath, directory.Root)
33+
if newContent != "" {
34+
directory.Content = append(directory.Content, newContent)
35+
}
36+
return nil
37+
}
3038

31-
var dir Directory
32-
err = json.Unmarshal(dirfile, &dir)
33-
if err != nil {
34-
return Directory{}, err
39+
err = filepath.Walk(path, walkFn)
40+
} else {
41+
contents, err := ioutil.ReadDir(path)
42+
if err != nil {
43+
return directory, err
44+
}
45+
46+
for _, file := range contents {
47+
fileName := "/" + file.Name()
48+
directory.Content = append(directory.Content, fileName)
49+
}
3550
}
36-
return dir, nil
51+
return directory, err
3752
}
3853

3954
// Checks for content differences between files of the same name from different directories
@@ -53,6 +68,22 @@ func GetModifiedEntries(d1, d2 Directory) []string {
5368
glog.Errorf("Error checking directory entry %s: %s\n", f, err)
5469
continue
5570
}
71+
f2stat, err := os.Stat(f2path)
72+
if err != nil {
73+
glog.Errorf("Error checking directory entry %s: %s\n", f, err)
74+
continue
75+
}
76+
77+
// If the directory entry in question is a tar, verify that the two have the same size
78+
if isTar(f1path) {
79+
if f1stat.Size() != f2stat.Size() {
80+
modified = append(modified, f)
81+
}
82+
continue
83+
}
84+
85+
// If the directory entry is not a tar and not a directory, then it's a file so make sure the file contents are the same
86+
// Note: We skip over directory entries because to compare directories, we compare their contents
5687
if !f1stat.IsDir() {
5788
same, err := checkSameFile(f1path, f2path)
5889
if err != nil {
@@ -83,12 +114,20 @@ type DirDiff struct {
83114
Mods []string
84115
}
85116

86-
func compareDirEntries(d1, d2 Directory) DirDiff {
117+
// DiffDirectory takes the diff of two directories, assuming both are completely unpacked
118+
func DiffDirectory(d1, d2 Directory) (DirDiff, bool) {
87119
adds := GetAddedEntries(d1, d2)
88120
dels := GetDeletedEntries(d1, d2)
89121
mods := GetModifiedEntries(d1, d2)
90122

91-
return DirDiff{d1.Root, d2.Root, adds, dels, mods}
123+
var same bool
124+
if len(adds) == 0 && len(dels) == 0 && len(mods) == 0 {
125+
same = true
126+
} else {
127+
same = false
128+
}
129+
130+
return DirDiff{d1.Root, d2.Root, adds, dels, mods}, same
92131
}
93132

94133
func checkSameFile(f1name, f2name string) (bool, error) {
@@ -121,7 +160,3 @@ func checkSameFile(f1name, f2name string) (bool, error) {
121160
}
122161
return true, nil
123162
}
124-
125-
func DiffDirectory(d1, d2 Directory) DirDiff {
126-
return compareDirEntries(d1, d2)
127-
}

utils/fs_utils_test.go

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -68,25 +68,42 @@ func TestGetModifiedEntries(t *testing.T) {
6868
}
6969

7070
func TestGetDirectory(t *testing.T) {
71-
type dirtestpair struct {
72-
input string
73-
expected_success bool
71+
tests := []struct {
72+
descrip string
73+
path string
74+
expected Directory
75+
deep bool
76+
}{
77+
{
78+
descrip: "deep",
79+
path: "testTars/la-croix3-full",
80+
expected: Directory{
81+
Root: "testTars/la-croix3-full",
82+
Content: []string{"/lime.txt", "/nest", "/nest/f1.txt", "/nested-dir", "/nested-dir/f2.txt", "/passionfruit.txt", "/peach-pear.txt"},
83+
},
84+
deep: true,
85+
},
86+
{
87+
descrip: "shallow",
88+
path: "testTars/la-croix3-full",
89+
expected: Directory{
90+
Root: "testTars/la-croix3-full",
91+
Content: []string{"/lime.txt", "/nest", "/nested-dir", "/passionfruit.txt", "/peach-pear.txt"},
92+
},
93+
deep: false,
94+
},
7495
}
96+
for _, testCase := range tests {
97+
dir, err := GetDirectory(testCase.path, testCase.deep)
98+
if err != nil {
99+
t.Errorf("Error converting directory to Directory struct")
100+
}
75101

76-
var dirtests = []dirtestpair{
77-
{"test_files/dir.json", true},
78-
{"test_files/dir_bad.json", false},
79-
{"nonexistentpath", false},
80-
{"", false},
81-
}
102+
actualDir := dir
103+
expectedDir := testCase.expected
82104

83-
for _, test := range dirtests {
84-
_, err := GetDirectory(test.input)
85-
if err != nil && test.expected_success {
86-
t.Errorf("Got unexpected error: %s", err)
87-
}
88-
if err == nil && !test.expected_success {
89-
t.Errorf("Expected error but got none")
105+
if !reflect.DeepEqual(actualDir, expectedDir) {
106+
t.Errorf("%s test was incorrect\nExpected: %s\nGot: %s", testCase.descrip, expectedDir, actualDir)
90107
}
91108
}
92109
}

0 commit comments

Comments
 (0)