621164bc84
After iterating on the GRPC API, the changes required for the actual implementation are now included in the content store. The begin change is the move to a single, atomic `Ingester.Writer` method for locking content ingestion on a key. From this, comes several new interface definitions. The main benefit here is the clarification between `Status` and `Info` that came out of the GPRC API. `Status` tells the status of a write, whereas `Info` is for querying metadata about various blobs. Signed-off-by: Stephen J Day <stephen.day@docker.com>
58 lines
1.2 KiB
Go
58 lines
1.2 KiB
Go
package content
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"io/ioutil"
|
|
|
|
"github.com/opencontainers/go-digest"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// WriteBlob writes data with the expected digest into the content store. If
|
|
// expected already exists, the method returns immediately and the reader will
|
|
// not be consumed.
|
|
//
|
|
// This is useful when the digest and size are known beforehand.
|
|
//
|
|
// Copy is buffered, so no need to wrap reader in buffered io.
|
|
func WriteBlob(ctx context.Context, cs Ingester, r io.Reader, ref string, size int64, expected digest.Digest) error {
|
|
cw, err := cs.Writer(ctx, ref)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ws, err := cw.Status()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if ws.Offset > 0 {
|
|
// Arbitrary limitation for now. We can detect io.Seeker on r and
|
|
// resume.
|
|
return errors.Errorf("cannot resume already started write")
|
|
}
|
|
|
|
buf := bufPool.Get().([]byte)
|
|
defer bufPool.Put(buf)
|
|
|
|
nn, err := io.CopyBuffer(cw, r, buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if size > 0 && nn != size {
|
|
return errors.Errorf("failed size verification: %v != %v", nn, size)
|
|
}
|
|
|
|
if err := cw.Commit(size, expected); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func readFileString(path string) (string, error) {
|
|
p, err := ioutil.ReadFile(path)
|
|
return string(p), err
|
|
}
|