Skip to content

Commit

Permalink
Unexport layout.WriteLayer; Implement suggested changes for validat…
Browse files Browse the repository at this point in the history
…eLayers and temp file naming
  • Loading branch information
ben-krieger committed Jan 3, 2022
1 parent 1764b92 commit 1f2b55a
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 26 deletions.
28 changes: 13 additions & 15 deletions pkg/v1/layout/write.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ package layout

import (
"bytes"
"crypto/rand"
"encoding/hex"
"encoding/json"
"io"
"io/ioutil"
Expand Down Expand Up @@ -238,11 +236,12 @@ func (l Path) writeBlob(hash v1.Hash, size int64, r io.ReadCloser, renamer func(
return nil
}

// If a rename func was provided, use a temporary file suffix
// If a renamer func was provided write to a temporary file
open := func() (*os.File, error) { return os.Create(file) }
if renamer != nil {
file += ".tmp"
open = func() (*os.File, error) { return ioutil.TempFile(dir, hash.Hex) }
}
w, err := os.Create(file)
w, err := open()
if err != nil {
return err
}
Expand All @@ -256,31 +255,30 @@ func (l Path) writeBlob(hash v1.Hash, size int64, r io.ReadCloser, renamer func(
if err != nil {
return err
}
renamePath := l.path("blobs", finalHash.Algorithm, finalHash.Hex)

// Always close file before renaming
if err := w.Close(); err != nil {
return err
}
return os.Rename(file, l.path("blobs", finalHash.Algorithm, finalHash.Hex))
return os.Rename(w.Name(), renamePath)
}

// WriteLayer writes the compressed layer to a blob. Unlike WriteBlob it will
// writeLayer writes the compressed layer to a blob. Unlike WriteBlob it will
// write to a temporary file (suffixed with .tmp) within the layout until the
// compressed reader is fully consumed and written to disk. Also unlike
// WriteBlob, it will not skip writing and exit without error when a blob file
// exists, but does not have the correct size. (The blob hash is not
// considered, because it may be expensive to compute.)
func (l Path) WriteLayer(layer v1.Layer) error {
func (l Path) writeLayer(layer v1.Layer) error {
d, err := layer.Digest()
if err != nil {
// Allow digest errors, since streams may not have calculated the hash
// yet. Instead, use a random value for the digest and require it to be
// yet. Instead, use an empty value, which will be transformed into a
// random file name with `ioutil.TempFile` and the final digest will be
// calculated after writing to a temp file and before renaming to the
// final path.
var randHash [32]byte
if _, err := rand.Read(randHash[:]); err != nil {
return err
}
d = v1.Hash{Algorithm: "sha256", Hex: hex.EncodeToString(randHash[:])}
d = v1.Hash{Algorithm: "sha256", Hex: ""}
}

s, err := layer.Size()
Expand Down Expand Up @@ -332,7 +330,7 @@ func (l Path) WriteImage(img v1.Image) error {
for _, layer := range layers {
layer := layer
g.Go(func() error {
return l.WriteLayer(layer)
return l.writeLayer(layer)
})
}
if err := g.Wait(); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/v1/layout/write_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,7 @@ func TestOverwriteWithWriteLayer(t *testing.T) {
}

// try writing expected contents with WriteLayer
if err := l.WriteLayer(layer); err != nil {
if err := l.writeLayer(layer); err != nil {
t.Fatalf("error attempting to overwrite truncated layer with valid layer; (Path).WriteLayer = %v", err)
}

Expand Down
27 changes: 17 additions & 10 deletions pkg/v1/validate/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,23 +114,20 @@ func validateLayers(img v1.Image, opt ...Option) error {
return layersExist(layers)
}

cf, err := img.ConfigFile()
if err != nil {
return err
}

m, err := img.Manifest()
if err != nil {
return err
}

digests := []v1.Hash{}
diffids := []v1.Hash{}
udiffids := []v1.Hash{}
sizes := []int64{}
for i, layer := range layers {
cl, err := computeLayer(layer)
if errors.Is(err, io.ErrUnexpectedEOF) {
// Errored while reading tar content of layer because a header or
// content section was not the correct length. This is most likely
// due to an incomplete download or otherwise interrupted process.
m, err := img.Manifest()
if err != nil {
return fmt.Errorf("undersized layer[%d] content", i)
}
return fmt.Errorf("undersized layer[%d] content: Manifest.Layers[%d].Size=%d", i, i, m.Layers[i].Size)
}
if err != nil {
Expand All @@ -144,6 +141,16 @@ func validateLayers(img v1.Image, opt ...Option) error {
sizes = append(sizes, cl.size)
}

cf, err := img.ConfigFile()
if err != nil {
return err
}

m, err := img.Manifest()
if err != nil {
return err
}

errs := []string{}
for i, layer := range layers {
digest, err := layer.Digest()
Expand Down

0 comments on commit 1f2b55a

Please sign in to comment.