Skip to content

Commit 8722df7

Browse files
authored
Merge pull request #7 from coolljt0725/remove_failure
Cleanup partially-unpacked directories on failures
2 parents f66ae09 + e88cd3a commit 8722df7

File tree

2 files changed

+82
-1
lines changed

2 files changed

+82
-1
lines changed

image/manifest.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,24 @@ func (m *manifest) validate(w walker) error {
8888
return nil
8989
}
9090

91-
func (m *manifest) unpack(w walker, dest string) error {
91+
func (m *manifest) unpack(w walker, dest string) (retErr error) {
92+
// error out if the dest directory is not empty
93+
s, err := ioutil.ReadDir(dest)
94+
if err != nil && !os.IsNotExist(err) {
95+
return errors.Wrap(err, "unable to open file") // err contains dest
96+
}
97+
if len(s) > 0 {
98+
return fmt.Errorf("%s is not empty", dest)
99+
}
100+
defer func() {
101+
// if we encounter error during unpacking
102+
// clean up the partially-unpacked destination
103+
if retErr != nil {
104+
if err := os.RemoveAll(dest); err != nil {
105+
fmt.Printf("Error: failed to remove partially-unpacked destination %v", err)
106+
}
107+
}
108+
}()
92109
for _, d := range m.Layers {
93110
if d.MediaType != string(schema.MediaTypeImageLayer) {
94111
continue

image/manifest_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,67 @@ func TestUnpackLayer(t *testing.T) {
122122
t.Fatal(err)
123123
}
124124
}
125+
126+
func TestUnpackLayerRemovePartialyUnpackedFile(t *testing.T) {
127+
// generate a tar file has duplicate entry which will failed on unpacking
128+
tmp1, err := ioutil.TempDir("", "test-layer")
129+
if err != nil {
130+
t.Fatal(err)
131+
}
132+
defer os.RemoveAll(tmp1)
133+
err = os.MkdirAll(filepath.Join(tmp1, "blobs", "sha256"), 0700)
134+
if err != nil {
135+
t.Fatal(err)
136+
}
137+
tarfile := filepath.Join(tmp1, "blobs", "sha256", "test.tar")
138+
f, err := os.Create(tarfile)
139+
if err != nil {
140+
t.Fatal(err)
141+
}
142+
143+
gw := gzip.NewWriter(f)
144+
tw := tar.NewWriter(gw)
145+
146+
tw.WriteHeader(&tar.Header{Name: "test", Size: 4, Mode: 0600})
147+
io.Copy(tw, bytes.NewReader([]byte("test")))
148+
tw.WriteHeader(&tar.Header{Name: "test", Size: 5, Mode: 0600})
149+
io.Copy(tw, bytes.NewReader([]byte("test1")))
150+
tw.Close()
151+
gw.Close()
152+
f.Close()
153+
154+
// generate sha256 hash
155+
h := sha256.New()
156+
file, err := os.Open(tarfile)
157+
if err != nil {
158+
t.Fatal(err)
159+
}
160+
defer file.Close()
161+
_, err = io.Copy(h, file)
162+
if err != nil {
163+
t.Fatal(err)
164+
}
165+
err = os.Rename(tarfile, filepath.Join(tmp1, "blobs", "sha256", fmt.Sprintf("%x", h.Sum(nil))))
166+
if err != nil {
167+
t.Fatal(err)
168+
}
169+
170+
testManifest := manifest{
171+
Layers: []descriptor{descriptor{
172+
MediaType: "application/vnd.oci.image.layer.tar+gzip",
173+
Digest: fmt.Sprintf("sha256:%s", fmt.Sprintf("%x", h.Sum(nil))),
174+
}},
175+
}
176+
err = testManifest.unpack(newPathWalker(tmp1), filepath.Join(tmp1, "rootfs"))
177+
if err != nil && !strings.Contains(err.Error(), "duplicate entry for") {
178+
t.Fatal(err)
179+
}
180+
181+
_, err = os.Stat(filepath.Join(tmp1, "rootfs"))
182+
if err != nil && !os.IsNotExist(err) {
183+
t.Fatal(err)
184+
}
185+
if err == nil {
186+
t.Fatal("Execpt partialy unpacked file has been removed")
187+
}
188+
}

0 commit comments

Comments
 (0)