Skip to content
This repository was archived by the owner on Aug 14, 2020. It is now read-only.

Commit 6a3f5bb

Browse files
committed
acirenderer: check recursive dependencies.
This patch checks and returns an error when a recursive dependency is detected. A recursive dependency is something like: A --- A or A --- B --- A Having the same image referenced by different branches isn't a recursion: A --- B --- C \-- C
1 parent 80ab01d commit 6a3f5bb

File tree

2 files changed

+308
-0
lines changed

2 files changed

+308
-0
lines changed

pkg/acirenderer/acirenderer_test.go

+294
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,300 @@ func TestGetUpperPWLM(t *testing.T) {
223223
}
224224
}
225225

226+
// Test dependency recursion.
227+
// This tests A --- A
228+
// If not checked it will cause an infinite recursion.
229+
func TestRecursiveDep1(t *testing.T) {
230+
dir, err := ioutil.TempDir("", tstprefix)
231+
if err != nil {
232+
t.Fatalf("error creating tempdir: %v", err)
233+
}
234+
defer os.RemoveAll(dir)
235+
ds := NewTestStore()
236+
237+
// B
238+
imj := `
239+
{
240+
"acKind": "ImageManifest",
241+
"acVersion": "0.1.1",
242+
"name": "example.com/test01"
243+
}
244+
`
245+
246+
imj, err = addDependencies(imj,
247+
types.Dependency{
248+
ImageName: "example.com/test01",
249+
},
250+
)
251+
if err != nil {
252+
t.Fatalf("unexpected error: %v", err)
253+
}
254+
255+
entries := []*testTarEntry{
256+
{
257+
contents: imj,
258+
header: &tar.Header{
259+
Name: "manifest",
260+
Size: int64(len(imj)),
261+
},
262+
},
263+
// An empty dir
264+
{
265+
header: &tar.Header{
266+
Name: "rootfs/a",
267+
Typeflag: tar.TypeDir,
268+
},
269+
},
270+
}
271+
272+
key1, err := newTestACI(entries, dir, ds)
273+
274+
expectedFiles := []*fileInfo{
275+
&fileInfo{path: "manifest", typeflag: tar.TypeReg},
276+
&fileInfo{path: "rootfs/a", typeflag: tar.TypeDir},
277+
}
278+
279+
err = checkRenderACI("example.com/test01", expectedFiles, ds)
280+
if err == nil {
281+
t.Fatalf("expected recursion error")
282+
} else {
283+
expectedErr := fmt.Errorf("recursion error, image with key %s already referenced by a parent image", key1)
284+
if err.Error() != expectedErr.Error() {
285+
t.Fatalf("expected error: %v, got: %v", expectedErr, err)
286+
}
287+
}
288+
}
289+
290+
// Test dependency recursion.
291+
// This tests A --- B --- A
292+
// If not checked it will cause an infinite recursion.
293+
func TestRecursiveDep2(t *testing.T) {
294+
dir, err := ioutil.TempDir("", tstprefix)
295+
if err != nil {
296+
t.Fatalf("error creating tempdir: %v", err)
297+
}
298+
defer os.RemoveAll(dir)
299+
ds := NewTestStore()
300+
301+
// B
302+
imj := `
303+
{
304+
"acKind": "ImageManifest",
305+
"acVersion": "0.1.1",
306+
"name": "example.com/test01"
307+
}
308+
`
309+
310+
// Make B depend on A
311+
imj, err = addDependencies(imj,
312+
types.Dependency{
313+
ImageName: "example.com/test02",
314+
},
315+
)
316+
if err != nil {
317+
t.Fatalf("unexpected error: %v", err)
318+
}
319+
320+
entries := []*testTarEntry{
321+
{
322+
contents: imj,
323+
header: &tar.Header{
324+
Name: "manifest",
325+
Size: int64(len(imj)),
326+
},
327+
},
328+
// An empty dir
329+
{
330+
header: &tar.Header{
331+
Name: "rootfs/a",
332+
Typeflag: tar.TypeDir,
333+
},
334+
},
335+
}
336+
337+
key1, err := newTestACI(entries, dir, ds)
338+
if err != nil {
339+
t.Fatalf("unexpected error: %v", err)
340+
}
341+
342+
// A
343+
imj = `
344+
{
345+
"acKind": "ImageManifest",
346+
"acVersion": "0.1.1",
347+
"name": "example.com/test02"
348+
}
349+
`
350+
351+
k1, _ := types.NewHash(key1)
352+
imj, err = addDependencies(imj,
353+
types.Dependency{
354+
ImageName: "example.com/test01",
355+
ImageID: k1},
356+
)
357+
if err != nil {
358+
t.Fatalf("unexpected error: %v", err)
359+
}
360+
361+
entries = []*testTarEntry{
362+
{
363+
contents: imj,
364+
header: &tar.Header{
365+
Name: "manifest",
366+
Size: int64(len(imj)),
367+
},
368+
},
369+
}
370+
371+
key2, err := newTestACI(entries, dir, ds)
372+
if err != nil {
373+
t.Fatalf("unexpected error: %v", err)
374+
}
375+
376+
expectedFiles := []*fileInfo{
377+
&fileInfo{path: "manifest", typeflag: tar.TypeReg},
378+
&fileInfo{path: "rootfs/a", typeflag: tar.TypeDir},
379+
}
380+
381+
err = checkRenderACI("example.com/test02", expectedFiles, ds)
382+
if err == nil {
383+
t.Fatalf("expected recursion error")
384+
} else {
385+
expectedErr := fmt.Errorf("recursion error, image with key %s already referenced by a parent image", key2)
386+
if err.Error() != expectedErr.Error() {
387+
t.Fatalf("expected error: %v, got: %v", expectedErr, err)
388+
}
389+
}
390+
}
391+
392+
// Having an image referenced inside different branches is not a recursion problem
393+
// This tests this dep tree:
394+
// A --- B --- C
395+
// \-- C
396+
func TestNonRecursiveDep(t *testing.T) {
397+
dir, err := ioutil.TempDir("", tstprefix)
398+
if err != nil {
399+
t.Fatalf("error creating tempdir: %v", err)
400+
}
401+
defer os.RemoveAll(dir)
402+
ds := NewTestStore()
403+
404+
// C
405+
imj := `
406+
{
407+
"acKind": "ImageManifest",
408+
"acVersion": "0.1.1",
409+
"name": "example.com/test01"
410+
}
411+
`
412+
413+
entries := []*testTarEntry{
414+
{
415+
contents: imj,
416+
header: &tar.Header{
417+
Name: "manifest",
418+
Size: int64(len(imj)),
419+
},
420+
},
421+
// An empty dir
422+
{
423+
header: &tar.Header{
424+
Name: "rootfs/a",
425+
Typeflag: tar.TypeDir,
426+
},
427+
},
428+
}
429+
430+
key1, err := newTestACI(entries, dir, ds)
431+
if err != nil {
432+
t.Fatalf("unexpected error: %v", err)
433+
}
434+
435+
// B
436+
imj = `
437+
{
438+
"acKind": "ImageManifest",
439+
"acVersion": "0.1.1",
440+
"name": "example.com/test02"
441+
}
442+
`
443+
444+
k1, _ := types.NewHash(key1)
445+
imj, err = addDependencies(imj,
446+
// C
447+
types.Dependency{
448+
ImageName: "example.com/test01",
449+
ImageID: k1},
450+
)
451+
if err != nil {
452+
t.Fatalf("unexpected error: %v", err)
453+
}
454+
455+
entries = []*testTarEntry{
456+
{
457+
contents: imj,
458+
header: &tar.Header{
459+
Name: "manifest",
460+
Size: int64(len(imj)),
461+
},
462+
},
463+
}
464+
465+
key2, err := newTestACI(entries, dir, ds)
466+
if err != nil {
467+
t.Fatalf("unexpected error: %v", err)
468+
}
469+
470+
// A
471+
imj = `
472+
{
473+
"acKind": "ImageManifest",
474+
"acVersion": "0.1.1",
475+
"name": "example.com/test03"
476+
}
477+
`
478+
479+
k2, _ := types.NewHash(key2)
480+
imj, err = addDependencies(imj,
481+
// B
482+
types.Dependency{
483+
ImageName: "example.com/test02",
484+
ImageID: k2},
485+
// C
486+
types.Dependency{
487+
ImageName: "example.com/test01",
488+
ImageID: k1},
489+
)
490+
if err != nil {
491+
t.Fatalf("unexpected error: %v", err)
492+
}
493+
494+
entries = []*testTarEntry{
495+
{
496+
contents: imj,
497+
header: &tar.Header{
498+
Name: "manifest",
499+
Size: int64(len(imj)),
500+
},
501+
},
502+
}
503+
504+
_, err = newTestACI(entries, dir, ds)
505+
if err != nil {
506+
t.Fatalf("unexpected error: %v", err)
507+
}
508+
509+
expectedFiles := []*fileInfo{
510+
&fileInfo{path: "manifest", typeflag: tar.TypeReg},
511+
&fileInfo{path: "rootfs/a", typeflag: tar.TypeDir},
512+
}
513+
514+
err = checkRenderACI("example.com/test03", expectedFiles, ds)
515+
if err != nil {
516+
t.Fatalf("unexpected error: %v", err)
517+
}
518+
}
519+
226520
// Test an image with 1 dep. The parent provides a dir not provided by the image.
227521
func TestDirFromParent(t *testing.T) {
228522
dir, err := ioutil.TempDir("", tstprefix)

pkg/acirenderer/resolve.go

+14
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package acirenderer
1616

1717
import (
1818
"container/list"
19+
"fmt"
1920

2021
"github.com/appc/spec/schema/types"
2122
)
@@ -75,6 +76,19 @@ func createDepList(key string, ap ACIRegistry) (Images, error) {
7576
if err != nil {
7677
return nil, err
7778
}
79+
// Check that an upper image is not the same as this dependency (same key)
80+
// Walk up the current branch and check that this depKey is not already in this branch
81+
curlevel := img.Level + 1
82+
for tel := el; tel != nil; tel = tel.Prev() {
83+
timg := tel.Value.(Image)
84+
if timg.Level < curlevel {
85+
if depKey == timg.Key {
86+
return nil, fmt.Errorf("recursion error, image with key %s already referenced by a parent image", depKey)
87+
}
88+
}
89+
curlevel = timg.Level
90+
}
91+
7892
depimg = Image{Im: im, Key: depKey, Level: img.Level + 1}
7993
imgsl.InsertAfter(depimg, el)
8094
}

0 commit comments

Comments
 (0)