From 91849080758e2c2dee3d7253f7fd0d3a40f0ec19 Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Wed, 13 Dec 2017 18:24:08 -0500 Subject: [PATCH] Fix image pull after a failure When resuming from a failed pull writer.Truncate() was not seeking to the proper position in the file. This caused writes to happen after the previously written content, instead of at the start of the file. Signed-off-by: Daniel Nephin --- content/helpers.go | 2 +- content/local/store_test.go | 40 +++++++++++++++++++++++++++++++++++++ content/local/writer.go | 4 ++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/content/helpers.go b/content/helpers.go index 86b853685bf4..83c31d917e1e 100644 --- a/content/helpers.go +++ b/content/helpers.go @@ -77,7 +77,7 @@ func Copy(ctx context.Context, cw Writer, r io.Reader, size int64, expected dige r, err = seekReader(r, ws.Offset, size) if err != nil { if !isUnseekable(err) { - return errors.Wrapf(err, "unabled to resume write to %v", ws.Ref) + return errors.Wrapf(err, "unable to resume write to %v", ws.Ref) } // reader is unseekable, try to move the writer back to the start. diff --git a/content/local/store_test.go b/content/local/store_test.go index 55a96189bbda..5c505240cf63 100644 --- a/content/local/store_test.go +++ b/content/local/store_test.go @@ -22,6 +22,7 @@ import ( "github.com/containerd/containerd/content/testsuite" "github.com/containerd/containerd/testutil" "github.com/opencontainers/go-digest" + "github.com/stretchr/testify/require" ) type memoryLabelStore struct { @@ -335,3 +336,42 @@ func checkWrite(ctx context.Context, t checker, cs content.Store, dgst digest.Di return dgst } + +func TestWriterTruncateRecoversFromIncompleteWrite(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "test-local-content-store-recover") + require.NoError(t, err) + defer os.RemoveAll(tmpdir) + + cs, err := NewStore(tmpdir) + require.NoError(t, err) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + ref := "ref" + content := []byte("this is the content") + total := int64(len(content)) + setupIncompleteWrite(ctx, t, cs, ref, total) + + writer, err := cs.Writer(ctx, ref, total, "") + require.NoError(t, err) + + require.NoError(t, writer.Truncate(0)) + + _, err = writer.Write(content) + require.NoError(t, err) + + dgst := digest.FromBytes(content) + err = writer.Commit(ctx, total, dgst) + require.NoError(t, err) +} + +func setupIncompleteWrite(ctx context.Context, t *testing.T, cs content.Store, ref string, total int64) { + writer, err := cs.Writer(ctx, ref, total, "") + require.NoError(t, err) + + _, err = writer.Write([]byte("bad data")) + require.NoError(t, err) + + require.NoError(t, writer.Close()) +} diff --git a/content/local/writer.go b/content/local/writer.go index fbf39f755a5c..e6b4276b421a 100644 --- a/content/local/writer.go +++ b/content/local/writer.go @@ -2,6 +2,7 @@ package local import ( "context" + "io" "os" "path/filepath" "runtime" @@ -167,5 +168,8 @@ func (w *writer) Truncate(size int64) error { } w.offset = 0 w.digester.Hash().Reset() + if _, err := w.fp.Seek(0, io.SeekStart); err != nil { + return err + } return w.fp.Truncate(0) }