504e67b867
Generate a hash chain involving the image configuration, layer digests, and parent image hashes. Use the digests to compute IDs for each image in a manifest, instead of using the remotely specified IDs. To avoid breaking users' caches, check for images already in the graph under old IDs, and avoid repulling an image if the version on disk under the legacy ID ends up with the same digest that was computed from the manifest for that image. When a calculated ID already exists in the graph but can't be verified, continue trying SHA256(digest) until a suitable ID is found. "save" and "load" are not changed to use a similar scheme. "load" will preserve the IDs present in the tar file. Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
132 lines
3.1 KiB
Go
132 lines
3.1 KiB
Go
// +build linux windows
|
|
|
|
package graph
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
"github.com/docker/docker/image"
|
|
"github.com/docker/docker/pkg/archive"
|
|
"github.com/docker/docker/pkg/chrootarchive"
|
|
)
|
|
|
|
// Load uploads a set of images into the repository. This is the complementary of ImageExport.
|
|
// The input stream is an uncompressed tar ball containing images and metadata.
|
|
func (s *TagStore) Load(inTar io.ReadCloser, outStream io.Writer) error {
|
|
tmpImageDir, err := ioutil.TempDir("", "docker-import-")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer os.RemoveAll(tmpImageDir)
|
|
|
|
var (
|
|
repoDir = filepath.Join(tmpImageDir, "repo")
|
|
)
|
|
|
|
if err := os.Mkdir(repoDir, os.ModeDir); err != nil {
|
|
return err
|
|
}
|
|
images := s.graph.Map()
|
|
excludes := make([]string, len(images))
|
|
i := 0
|
|
for k := range images {
|
|
excludes[i] = k
|
|
i++
|
|
}
|
|
if err := chrootarchive.Untar(inTar, repoDir, &archive.TarOptions{ExcludePatterns: excludes}); err != nil {
|
|
return err
|
|
}
|
|
|
|
dirs, err := ioutil.ReadDir(repoDir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, d := range dirs {
|
|
if d.IsDir() {
|
|
if err := s.recursiveLoad(d.Name(), tmpImageDir); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
reposJSONFile, err := os.Open(filepath.Join(tmpImageDir, "repo", "repositories"))
|
|
if err != nil {
|
|
if !os.IsNotExist(err) {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
defer reposJSONFile.Close()
|
|
|
|
repositories := map[string]Repository{}
|
|
if err := json.NewDecoder(reposJSONFile).Decode(&repositories); err != nil {
|
|
return err
|
|
}
|
|
|
|
for imageName, tagMap := range repositories {
|
|
for tag, address := range tagMap {
|
|
if err := s.setLoad(imageName, tag, address, true, outStream); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *TagStore) recursiveLoad(address, tmpImageDir string) error {
|
|
if _, err := s.LookupImage(address); err != nil {
|
|
logrus.Debugf("Loading %s", address)
|
|
|
|
imageJSON, err := ioutil.ReadFile(filepath.Join(tmpImageDir, "repo", address, "json"))
|
|
if err != nil {
|
|
logrus.Debugf("Error reading json: %v", err)
|
|
return err
|
|
}
|
|
|
|
layer, err := os.Open(filepath.Join(tmpImageDir, "repo", address, "layer.tar"))
|
|
if err != nil {
|
|
logrus.Debugf("Error reading embedded tar: %v", err)
|
|
return err
|
|
}
|
|
img, err := image.NewImgJSON(imageJSON)
|
|
if err != nil {
|
|
logrus.Debugf("Error unmarshalling json: %v", err)
|
|
return err
|
|
}
|
|
if err := image.ValidateID(img.ID); err != nil {
|
|
logrus.Debugf("Error validating ID: %v", err)
|
|
return err
|
|
}
|
|
|
|
// ensure no two downloads of the same layer happen at the same time
|
|
poolKey := "layer:" + img.ID
|
|
broadcaster, found := s.poolAdd("pull", poolKey)
|
|
if found {
|
|
logrus.Debugf("Image (id: %s) load is already running, waiting", img.ID)
|
|
return broadcaster.Wait()
|
|
}
|
|
|
|
defer s.poolRemove("pull", poolKey)
|
|
|
|
if img.Parent != "" {
|
|
if !s.graph.Exists(img.Parent) {
|
|
if err := s.recursiveLoad(img.Parent, tmpImageDir); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
if err := s.graph.Register(v1Descriptor{img}, layer); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
logrus.Debugf("Completed processing %s", address)
|
|
|
|
return nil
|
|
}
|