Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calculate checksum when installing image layers #8249

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion daemon/graphdriver/aufs/aufs.go
Expand Up @@ -300,11 +300,14 @@ func (a *Driver) Diff(id, parent string) (archive.Archive, error) {
// AUFS doesn't need the parent layer to produce a diff.
return archive.TarWithOptions(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
Compression: archive.Uncompressed,
Excludes: []string{".wh..wh.*"},
})
}

func (a *Driver) applyDiff(id string, diff archive.ArchiveReader) error {
return archive.Untar(diff, path.Join(a.rootPath(), "diff", id), nil)
return archive.Untar(diff, path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
Excludes: []string{".wh..wh."},
})
}

// DiffSize calculates the changes between the specified id
Expand Down
6 changes: 3 additions & 3 deletions graph/graph.go
Expand Up @@ -132,14 +132,14 @@ func (graph *Graph) Create(layerData archive.ArchiveReader, containerID, contain
img.ContainerConfig = *containerConfig
}

if err := graph.Register(img, nil, layerData); err != nil {
if err := graph.Register(img, layerData); err != nil {
return nil, err
}
return img, nil
}

// Register imports a pre-existing image into the graph.
func (graph *Graph) Register(img *image.Image, jsonData []byte, layerData archive.ArchiveReader) (err error) {
func (graph *Graph) Register(img *image.Image, layerData archive.ArchiveReader) (err error) {
defer func() {
// If any error occurs, remove the new dir from the driver.
// Don't check for errors since the dir might not have been created.
Expand Down Expand Up @@ -181,7 +181,7 @@ func (graph *Graph) Register(img *image.Image, jsonData []byte, layerData archiv
}
// Apply the diff/layer
img.SetGraph(graph)
if err := image.StoreImage(img, jsonData, layerData, tmp); err != nil {
if err := image.StoreImage(img, layerData, tmp); err != nil {
return err
}
// Commit
Expand Down
2 changes: 1 addition & 1 deletion graph/load.go
Expand Up @@ -118,7 +118,7 @@ func (s *TagStore) recursiveLoad(eng *engine.Engine, address, tmpImageDir string
}
}
}
if err := s.graph.Register(img, imageJson, layer); err != nil {
if err := s.graph.Register(img, layer); err != nil {
return err
}
}
Expand Down
5 changes: 2 additions & 3 deletions graph/pull.go
Expand Up @@ -392,8 +392,7 @@ func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint
layers_downloaded = true
defer layer.Close()

err = s.graph.Register(img, imgJSON,
utils.ProgressReader(layer, imgSize, out, sf, false, utils.TruncateID(id), "Downloading"))
err = s.graph.Register(img, utils.ProgressReader(layer, imgSize, out, sf, false, utils.TruncateID(id), "Downloading"))
if terr, ok := err.(net.Error); ok && terr.Timeout() && j < retries {
time.Sleep(time.Duration(j) * 500 * time.Millisecond)
continue
Expand Down Expand Up @@ -577,7 +576,7 @@ func (s *TagStore) pullV2Tag(eng *engine.Engine, r *registry.Session, out io.Wri
defer d.tmpFile.Close()
d.tmpFile.Seek(0, 0)
if d.tmpFile != nil {
err = s.graph.Register(d.img, d.imgJSON,
err = s.graph.Register(d.img,
utils.ProgressReader(d.tmpFile, int(d.length), out, sf, false, utils.TruncateID(d.img.ID), "Extracting"))
if err != nil {
return false, err
Expand Down
50 changes: 1 addition & 49 deletions graph/service.go
Expand Up @@ -6,12 +6,10 @@ import (

log "github.com/Sirupsen/logrus"
"github.com/docker/docker/engine"
"github.com/docker/docker/image"
)

func (s *TagStore) Install(eng *engine.Engine) error {
for name, handler := range map[string]engine.Handler{
"image_set": s.CmdSet,
"image_tag": s.CmdTag,
"tag": s.CmdTagLegacy, // FIXME merge with "image_tag"
"image_get": s.CmdGet,
Expand All @@ -33,53 +31,6 @@ func (s *TagStore) Install(eng *engine.Engine) error {
return nil
}

// CmdSet stores a new image in the graph.
// Images are stored in the graph using 4 elements:
// - A user-defined ID
// - A collection of metadata describing the image
// - A directory tree stored as a tar archive (also called the "layer")
// - A reference to a "parent" ID on top of which the layer should be applied
//
// NOTE: even though the parent ID is only useful in relation to the layer and how
// to apply it (ie you could represent the full directory tree as 'parent_layer + layer',
// it is treated as a top-level property of the image. This is an artifact of early
// design and should probably be cleaned up in the future to simplify the design.
//
// Syntax: image_set ID
// Input:
// - Layer content must be streamed in tar format on stdin. An empty input is
// valid and represents a nil layer.
//
// - Image metadata must be passed in the command environment.
// 'json': a json-encoded object with all image metadata.
// It will be stored as-is, without any encoding/decoding artifacts.
// That is a requirement of the current registry client implementation,
// because a re-encoded json might invalidate the image checksum at
// the next upload, even with functionaly identical content.
func (s *TagStore) CmdSet(job *engine.Job) engine.Status {
if len(job.Args) != 1 {
return job.Errorf("usage: %s NAME", job.Name)
}
var (
imgJSON = []byte(job.Getenv("json"))
layer = job.Stdin
)
if len(imgJSON) == 0 {
return job.Errorf("mandatory key 'json' is not set")
}
// We have to pass an *image.Image object, even though it will be completely
// ignored in favor of the redundant json data.
// FIXME: the current prototype of Graph.Register is stupid and redundant.
img, err := image.NewImgJSON(imgJSON)
if err != nil {
return job.Error(err)
}
if err := s.graph.Register(img, imgJSON, layer); err != nil {
return job.Error(err)
}
return engine.StatusOK
}

// CmdGet returns information about an image.
// If the image doesn't exist, an empty object is returned, to allow
// checking for an image's existence.
Expand Down Expand Up @@ -150,6 +101,7 @@ func (s *TagStore) CmdLookup(job *engine.Job) engine.Status {
out.Set("Os", image.OS)
out.SetInt64("Size", image.Size)
out.SetInt64("VirtualSize", image.GetParentsSize(0)+image.Size)
out.Set("Checksum", image.Checksum)
if _, err = out.WriteTo(job.Stdout); err != nil {
return job.Error(err)
}
Expand Down
2 changes: 1 addition & 1 deletion graph/tags_unit_test.go
Expand Up @@ -62,7 +62,7 @@ func mkTestTagStore(root string, t *testing.T) *TagStore {
t.Fatal(err)
}
img := &image.Image{ID: testImageID}
if err := graph.Register(img, nil, archive); err != nil {
if err := graph.Register(img, archive); err != nil {
t.Fatal(err)
}
if err := store.Set(testImageName, "", testImageID, false); err != nil {
Expand Down
44 changes: 30 additions & 14 deletions image/image.go
Expand Up @@ -11,6 +11,7 @@ import (

log "github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/tarsum"
"github.com/docker/docker/runconfig"
"github.com/docker/docker/utils"
)
Expand All @@ -32,6 +33,7 @@ type Image struct {
Config *runconfig.Config `json:"config,omitempty"`
Architecture string `json:"architecture,omitempty"`
OS string `json:"os,omitempty"`
Checksum string `json:"checksum"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we want this information to be pushed on the registry & available on docker inspect ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be pushed to the registry as part of the image json and available when inspecting an image and requesting raw json. We do want it available as it is very important to the goals for #8093

Size int64

graph Graph
Expand Down Expand Up @@ -70,7 +72,7 @@ func LoadImage(root string) (*Image, error) {
return img, nil
}

func StoreImage(img *Image, jsonData []byte, layerData archive.ArchiveReader, root string) error {
func StoreImage(img *Image, layerData archive.ArchiveReader, root string) error {
// Store the layer
var (
size int64
Expand All @@ -80,29 +82,43 @@ func StoreImage(img *Image, jsonData []byte, layerData archive.ArchiveReader, ro

// If layerData is not nil, unpack it into the new layer
if layerData != nil {
if size, err = driver.ApplyDiff(img.ID, img.Parent, layerData); err != nil {
// Autodetect compression of the layer data.
layerData, err := archive.DecompressStream(layerData)
if err != nil {
return err
}
defer layerData.Close()

// Wrap with tarsum.
tarsumLayerData, err := tarsum.NewTarSum(layerData, true, tarsum.VersionDev)
if err != nil {
return err
}

// Extract the archive to the file system driver.
if size, err = driver.ApplyDiff(img.ID, img.Parent, tarsumLayerData); err != nil {
return err
}

// Get the resulting tarsum.
img.Checksum = tarsumLayerData.Sum(nil)
}

img.Size = size
if err := img.SaveSize(root); err != nil {
return err
}

// If raw json is provided, then use it
if jsonData != nil {
if err := ioutil.WriteFile(jsonPath(root), jsonData, 0600); err != nil {
return err
}
} else {
if jsonData, err = json.Marshal(img); err != nil {
return err
}
if err := ioutil.WriteFile(jsonPath(root), jsonData, 0600); err != nil {
return err
}
var jsonData []byte

if jsonData, err = json.Marshal(img); err != nil {
return err
}

if err := ioutil.WriteFile(jsonPath(root), jsonData, 0600); err != nil {
return err
}

return nil
}

Expand Down
14 changes: 7 additions & 7 deletions integration/graph_test.go
Expand Up @@ -74,7 +74,7 @@ func TestInterruptedRegister(t *testing.T) {
Created: time.Now(),
}
w.CloseWithError(errors.New("But I'm not a tarball!")) // (Nobody's perfect, darling)
graph.Register(image, nil, badArchive)
graph.Register(image, badArchive)
if _, err := graph.Get(image.ID); err == nil {
t.Fatal("Image should not exist after Register is interrupted")
}
Expand All @@ -83,7 +83,7 @@ func TestInterruptedRegister(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if err := graph.Register(image, nil, goodArchive); err != nil {
if err := graph.Register(image, goodArchive); err != nil {
t.Fatal(err)
}
}
Expand Down Expand Up @@ -133,7 +133,7 @@ func TestRegister(t *testing.T) {
Comment: "testing",
Created: time.Now(),
}
err = graph.Register(image, nil, archive)
err = graph.Register(image, archive)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -228,7 +228,7 @@ func TestDelete(t *testing.T) {
t.Fatal(err)
}
// Test delete twice (pull -> rm -> pull -> rm)
if err := graph.Register(img1, nil, archive); err != nil {
if err := graph.Register(img1, archive); err != nil {
t.Fatal(err)
}
if err := graph.Delete(img1.ID); err != nil {
Expand Down Expand Up @@ -262,9 +262,9 @@ func TestByParent(t *testing.T) {
Created: time.Now(),
Parent: parentImage.ID,
}
_ = graph.Register(parentImage, nil, archive1)
_ = graph.Register(childImage1, nil, archive2)
_ = graph.Register(childImage2, nil, archive3)
_ = graph.Register(parentImage, archive1)
_ = graph.Register(childImage1, archive2)
_ = graph.Register(childImage2, archive3)

byParent, err := graph.ByParent()
if err != nil {
Expand Down