func getParent(file *os.File, imgID string, debug log.Logger) (string, error) { var parent string _, err := file.Seek(0, 0) if err != nil { return "", fmt.Errorf("error seeking file: %v", err) } jsonPath := filepath.Join(imgID, "json") parentWalker := func(t *tarball.TarFile) error { if filepath.Clean(t.Name()) == jsonPath { jsonb, err := ioutil.ReadAll(t.TarStream) if err != nil { return fmt.Errorf("error reading layer json: %v", err) } var dockerData types.DockerImageData if err := json.Unmarshal(jsonb, &dockerData); err != nil { return fmt.Errorf("error unmarshaling layer data: %v", err) } parent = dockerData.Parent } return nil } tr := tar.NewReader(file) if err := tarball.Walk(*tr, parentWalker); err != nil { return "", err } debug.Printf("Layer %q depends on layer %q", imgID, parent) return parent, nil }
// getAncestry computes an image ancestry, returning an ordered list // of dependencies starting from the topmost image to the base. // It checks for dependency loops via duplicate detection in the image // chain and errors out in such cases. func getAncestry(file *os.File, imgID string, debug log.Logger) ([]string, error) { var ancestry []string deps := make(map[string]bool) curImgID := imgID var err error for curImgID != "" { if deps[curImgID] { return nil, fmt.Errorf("dependency loop detected at image %q", curImgID) } deps[curImgID] = true ancestry = append(ancestry, curImgID) debug.Printf("Getting ancestry for layer %q", curImgID) curImgID, err = getParent(file, curImgID, debug) if err != nil { return nil, err } } return ancestry, nil }