Move Digest type into discrete package

The Digest type will be fairly central for blob and layer management. The type
presented in this package provides a number of core features that should enable
reliable use within the registry. This commit will be followed by others that
convert the storage layer and webapp to use this type as the primary layer/blob
CAS identifier.
This commit is contained in:
Stephen J Day 2014-11-19 13:23:01 -08:00
parent ce91eabaab
commit 3cfe9aede5
5 changed files with 479 additions and 0 deletions

131
digest/verifiers.go Normal file
View file

@ -0,0 +1,131 @@
package digest
import (
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"hash"
"io"
"io/ioutil"
"github.com/docker/docker/pkg/tarsum"
)
type Verifier interface {
io.Writer
// Verified will return true if the content written to Verifier matches
// the digest.
Verified() bool
// Planned methods:
// Err() error
// Reset()
}
func DigestVerifier(d Digest) Verifier {
alg := d.Algorithm()
switch alg {
case "md5", "sha1", "sha256":
return hashVerifier{
hash: newHash(alg),
digest: d,
}
default:
// Assume we have a tarsum.
version, err := tarsum.GetVersionFromTarsum(string(d))
if err != nil {
panic(err) // Always assume valid tarsum at this point.
}
pr, pw := io.Pipe()
// TODO(stevvooe): We may actually want to ban the earlier versions of
// tarsum. That decision may not be the place of the verifier.
ts, err := tarsum.NewTarSum(pr, true, version)
if err != nil {
panic(err)
}
// TODO(sday): Ick! A goroutine per digest verification? We'll have to
// get the tarsum library to export an io.Writer variant.
go func() {
io.Copy(ioutil.Discard, ts)
pw.Close()
}()
return &tarsumVerifier{
digest: d,
ts: ts,
pr: pr,
pw: pw,
}
}
panic("unsupported digest: " + d)
}
// LengthVerifier returns a verifier that returns true when the number of read
// bytes equals the expected parameter.
func LengthVerifier(expected int64) Verifier {
return &lengthVerifier{
expected: expected,
}
}
type lengthVerifier struct {
expected int64 // expected bytes read
len int64 // bytes read
}
func (lv *lengthVerifier) Write(p []byte) (n int, err error) {
n = len(p)
lv.len += int64(n)
return n, err
}
func (lv *lengthVerifier) Verified() bool {
return lv.expected == lv.len
}
func newHash(name string) hash.Hash {
switch name {
case "sha256":
return sha256.New()
case "sha1":
return sha1.New()
case "md5":
return md5.New()
default:
panic("unsupport algorithm: " + name)
}
}
type hashVerifier struct {
digest Digest
hash hash.Hash
}
func (hv hashVerifier) Write(p []byte) (n int, err error) {
return hv.hash.Write(p)
}
func (hv hashVerifier) Verified() bool {
return hv.digest == NewDigest(hv.digest.Algorithm(), hv.hash)
}
type tarsumVerifier struct {
digest Digest
ts tarsum.TarSum
pr *io.PipeReader
pw *io.PipeWriter
}
func (tv *tarsumVerifier) Write(p []byte) (n int, err error) {
return tv.pw.Write(p)
}
func (tv *tarsumVerifier) Verified() bool {
return tv.digest == Digest(tv.ts.Sum(nil))
}