diff --git a/pkg/repocli/digest.go b/pkg/repocli/digest.go index 5ec317c..739c4e5 100644 --- a/pkg/repocli/digest.go +++ b/pkg/repocli/digest.go @@ -29,14 +29,16 @@ type Algorithm int const ( Undefined Algorithm = iota SHA256 + SHA384 SHA512 ) func DigestType(digest string) Algorithm { var err error - var typ Algorithm + var algorithm Algorithm digest = strings.ToLower(digest) digest = strings.TrimPrefix(digest, "sha256:") + digest = strings.TrimPrefix(digest, "sha384:") digest = strings.TrimPrefix(digest, "sha512:") decoded, err := hex.DecodeString(digest) if err != nil { @@ -44,13 +46,15 @@ func DigestType(digest string) Algorithm { } switch len(decoded) { case 32: - typ = SHA256 + algorithm = SHA256 + case 48: + algorithm = SHA384 case 64: - typ = SHA512 + algorithm = SHA512 default: - typ = Undefined + algorithm = Undefined } - return typ + return algorithm } type Digest struct { @@ -64,6 +68,8 @@ func NewDigestFromBytes(algorithm Algorithm, payload []byte) *Digest { switch algorithm { case SHA512: hasher = sha512.New() + case SHA384: + hasher = sha512.New384() default: hasher = sha256.New() } @@ -82,6 +88,7 @@ func ParseDigest(str string) (*Digest, error) { str = strings.ToLower(str) str = strings.TrimPrefix(str, "sha256:") + str = strings.TrimPrefix(str, "sha384:") str = strings.TrimPrefix(str, "sha512:") decoded, err := hex.DecodeString(str) @@ -93,6 +100,8 @@ func ParseDigest(str string) (*Digest, error) { switch len(decoded) { case 32: digest.algorithm = SHA256 + case 48: + digest.algorithm = SHA384 case 64: digest.algorithm = SHA512 default: @@ -115,6 +124,8 @@ func (dig *Digest) Prefix() string { switch dig.algorithm { case SHA256: prefix = "sha256" + case SHA384: + prefix = "sha384" case SHA512: prefix = "sha512" } @@ -126,6 +137,8 @@ func (dig *Digest) Encoded() string { switch dig.algorithm { case SHA256: prefix = "sha256" + case SHA384: + prefix = "sha384" case SHA512: prefix = "sha512" } diff --git a/pkg/repocli/imager.go b/pkg/repocli/imager.go index e338516..d9eaea6 100644 --- a/pkg/repocli/imager.go +++ b/pkg/repocli/imager.go @@ -3,6 +3,7 @@ package repocli import ( "bytes" "context" + "encoding/json" "errors" "io" "os" @@ -12,19 +13,12 @@ import ( ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) -type Imageer interface { - WriteManifest(mime string, manifest []byte) error - ReadManifest(digest string) (bool, string, []byte, error) - WriteBlob(digest string, reader io.Reader) error - ReadBlob(digest string, writer io.Writer) error -} - type Imager struct { index ocispec.Index place string } -func NewImager(place string) *Imager { +func NewEmptyImager(place string) *Imager { imager := &Imager{ index: ocispec.Index{ MediaType: MediaTypeOIIv1, @@ -32,7 +26,7 @@ func NewImager(place string) *Imager { }, place: place, } - //imager.SchemaVersion = 2 + imager.index.SchemaVersion = 2 return imager } @@ -42,32 +36,30 @@ func (ima *Imager) WriteManifest(ctx context.Context, digest, mime string, paylo if err != nil { return err } - var subdir string - switch digestobj.Algorithm() { - case ocidigest.SHA256: - subdir = "sha256" - case ocidigest.SHA384: - subdir = "sha384" - case ocidigest.SHA512: - subdir = "sha512" - default: - err := errors.New("Unknown digest type") - return err - } + subdir := string(digestobj.Algorithm()) dir := filepath.Join(ima.place, subdir) err = os.MkdirAll(dir, 0750) if err != nil { return err } - fpath := filepath.Join(dir, digestobj.Encoded()) - file, err := os.OpenFile(fpath, os.O_CREATE|os.O_WRONLY, 0640) + mpath := filepath.Join(dir, digestobj.Encoded()) + mfile, err := os.OpenFile(mpath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0640) if err != nil { return err } + defer mfile.Close() reader := bytes.NewReader(payload) - size, err := io.Copy(file, reader) - if size != int64(len(payload)) { - err = errors.New("Mismatch sizes") + + verifier := digestobj.Verifier() + mwriter := io.MultiWriter(verifier, mfile) + recsize, err := io.Copy(mwriter, reader) + size := int64(len(payload)) + if size != recsize { + err = errors.New("Mismatch manigest sizes") + return err + } + if !verifier.Verified() { + err = errors.New("Mismatch manifest digests") return err } descr := ocispec.Descriptor{ @@ -76,7 +68,26 @@ func (ima *Imager) WriteManifest(ctx context.Context, digest, mime string, paylo Size: size, } ima.index.Manifests = append(ima.index.Manifests, descr) - // TODO: flush index + indexdat, err := json.Marshal(ima.index) + if err != nil { + return err + } + // Flush index + ipath := filepath.Join(dir, "index.json") + ifile, err := os.OpenFile(ipath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0640) + if err != nil { + return err + } + defer ifile.Close() + reader = bytes.NewReader(indexdat) + recsize, err = io.Copy(ifile, reader) + if err != nil { + return err + } + if recsize != int64(len(indexdat)) { + err = errors.New("Mismatch index sizes") + return err + } return err } @@ -86,31 +97,20 @@ func (ima *Imager) WriteLayer(ctx context.Context, digest string, size int64, re if err != nil { return err } - var subdir string - switch digestobj.Algorithm() { - case ocidigest.SHA256: - subdir = "sha256" - case ocidigest.SHA384: - subdir = "sha384" - case ocidigest.SHA512: - subdir = "sha512" - default: - err := errors.New("Unknown digest type") - return err - } + subdir := string(digestobj.Algorithm()) dir := filepath.Join(ima.place, subdir) err = os.MkdirAll(dir, 0750) if err != nil { return err } fpath := filepath.Join(dir, digestobj.Encoded()) - file, err := os.OpenFile(fpath, os.O_CREATE|os.O_WRONLY, 0640) + file, err := os.OpenFile(fpath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0640) if err != nil { return err } verifier := digestobj.Verifier() - mw := io.MultiWriter(verifier, file) - recsize, err := io.Copy(mw, reader) + mwriter := io.MultiWriter(verifier, file) + recsize, err := io.Copy(mwriter, reader) if size != recsize { err = errors.New("Mismatch sizes") return err diff --git a/pkg/repocli/imager_test.go b/pkg/repocli/imager_test.go index 605e785..adbc6cd 100644 --- a/pkg/repocli/imager_test.go +++ b/pkg/repocli/imager_test.go @@ -1,16 +1,14 @@ package repocli import ( - "github.com/stretchr/testify/require" - ocidigest "github.com/opencontainers/go-digest" + "github.com/stretchr/testify/require" "bytes" "context" "fmt" "testing" "time" - //"io" ) func TestDigest(t *testing.T) { @@ -23,7 +21,7 @@ func TestDigest(t *testing.T) { ctx, _ := context.WithTimeout(context.Background(), 10*time.Second) tmpdir := t.TempDir() - imager := NewImager(tmpdir) + imager := NewEmptyImager(tmpdir) require.NotNil(t, imager) digest := fmt.Sprintf("%s:%s", digestobj.Algorithm(), digestobj.Encoded()) size := int64(len(payload))