Skip to content

Commit

Permalink
Merge pull request moby#8869 from jlhawn/tarsum_version
Browse files Browse the repository at this point in the history
Finalize TarSum Version 1 w/ refactor
  • Loading branch information
unclejack committed Nov 13, 2014
2 parents b0d219e + a7aa2c8 commit 3aa2245
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 54 deletions.
60 changes: 12 additions & 48 deletions pkg/tarsum/tarsum.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import (
"encoding/hex"
"hash"
"io"
"sort"
"strconv"
"strings"

"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
Expand All @@ -29,18 +27,20 @@ const (
// including the byte payload of the image's json metadata as well, and for
// calculating the checksums for buildcache.
func NewTarSum(r io.Reader, dc bool, v Version) (TarSum, error) {
if _, ok := tarSumVersions[v]; !ok {
return nil, ErrVersionNotImplemented
headerSelector, err := getTarHeaderSelector(v)
if err != nil {
return nil, err
}
return &tarSum{Reader: r, DisableCompression: dc, tarSumVersion: v}, nil
return &tarSum{Reader: r, DisableCompression: dc, tarSumVersion: v, headerSelector: headerSelector}, nil
}

// Create a new TarSum, providing a THash to use rather than the DefaultTHash
func NewTarSumHash(r io.Reader, dc bool, v Version, tHash THash) (TarSum, error) {
if _, ok := tarSumVersions[v]; !ok {
return nil, ErrVersionNotImplemented
headerSelector, err := getTarHeaderSelector(v)
if err != nil {
return nil, err
}
return &tarSum{Reader: r, DisableCompression: dc, tarSumVersion: v, tHash: tHash}, nil
return &tarSum{Reader: r, DisableCompression: dc, tarSumVersion: v, headerSelector: headerSelector, tHash: tHash}, nil
}

// TarSum is the generic interface for calculating fixed time
Expand Down Expand Up @@ -69,8 +69,9 @@ type tarSum struct {
currentFile string
finished bool
first bool
DisableCompression bool // false by default. When false, the output gzip compressed.
tarSumVersion Version // this field is not exported so it can not be mutated during use
DisableCompression bool // false by default. When false, the output gzip compressed.
tarSumVersion Version // this field is not exported so it can not be mutated during use
headerSelector tarHeaderSelector // handles selecting and ordering headers for files in the archive
}

func (ts tarSum) Hash() THash {
Expand Down Expand Up @@ -103,49 +104,12 @@ type simpleTHash struct {
func (sth simpleTHash) Name() string { return sth.n }
func (sth simpleTHash) Hash() hash.Hash { return sth.h() }

func (ts tarSum) selectHeaders(h *tar.Header, v Version) (set [][2]string) {
for _, elem := range [][2]string{
{"name", h.Name},
{"mode", strconv.Itoa(int(h.Mode))},
{"uid", strconv.Itoa(h.Uid)},
{"gid", strconv.Itoa(h.Gid)},
{"size", strconv.Itoa(int(h.Size))},
{"mtime", strconv.Itoa(int(h.ModTime.UTC().Unix()))},
{"typeflag", string([]byte{h.Typeflag})},
{"linkname", h.Linkname},
{"uname", h.Uname},
{"gname", h.Gname},
{"devmajor", strconv.Itoa(int(h.Devmajor))},
{"devminor", strconv.Itoa(int(h.Devminor))},
} {
if v >= VersionDev && elem[0] == "mtime" {
continue
}
set = append(set, elem)
}
return
}

func (ts *tarSum) encodeHeader(h *tar.Header) error {
for _, elem := range ts.selectHeaders(h, ts.Version()) {
for _, elem := range ts.headerSelector.selectHeaders(h) {
if _, err := ts.h.Write([]byte(elem[0] + elem[1])); err != nil {
return err
}
}

// include the additional pax headers, from an ordered list
if ts.Version() >= VersionDev {
var keys []string
for k := range h.Xattrs {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
if _, err := ts.h.Write([]byte(k + h.Xattrs[k])); err != nil {
return err
}
}
}
return nil
}

Expand Down
86 changes: 81 additions & 5 deletions pkg/tarsum/versioning.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,23 @@ package tarsum

import (
"errors"
"sort"
"strconv"
"strings"

"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
)

// versioning of the TarSum algorithm
// based on the prefix of the hash used
// i.e. "tarsum+sha256:e58fcf7418d4390dec8e8fb69d88c06ec07039d651fedd3aa72af9972e7d046b"
type Version int

// Prefix of "tarsum"
const (
// Prefix of "tarsum"
Version0 Version = iota
// Prefix of "tarsum.dev"
// NOTE: this variable will be of an unsettled next-version of the TarSum calculation
Version1
// NOTE: this variable will be either the latest or an unsettled next-version of the TarSum calculation
VersionDev
)

Expand All @@ -28,8 +32,9 @@ func GetVersions() []Version {
}

var tarSumVersions = map[Version]string{
0: "tarsum",
1: "tarsum.dev",
Version0: "tarsum",
Version1: "tarsum.v1",
VersionDev: "tarsum.dev",
}

func (tsv Version) String() string {
Expand All @@ -50,7 +55,78 @@ func GetVersionFromTarsum(tarsum string) (Version, error) {
return -1, ErrNotVersion
}

// Errors that may be returned by functions in this package
var (
ErrNotVersion = errors.New("string does not include a TarSum Version")
ErrVersionNotImplemented = errors.New("TarSum Version is not yet implemented")
)

// tarHeaderSelector is the interface which different versions
// of tarsum should use for selecting and ordering tar headers
// for each item in the archive.
type tarHeaderSelector interface {
selectHeaders(h *tar.Header) (orderedHeaders [][2]string)
}

type tarHeaderSelectFunc func(h *tar.Header) (orderedHeaders [][2]string)

func (f tarHeaderSelectFunc) selectHeaders(h *tar.Header) (orderedHeaders [][2]string) {
return f(h)
}

func v0TarHeaderSelect(h *tar.Header) (orderedHeaders [][2]string) {
return [][2]string{
{"name", h.Name},
{"mode", strconv.Itoa(int(h.Mode))},
{"uid", strconv.Itoa(h.Uid)},
{"gid", strconv.Itoa(h.Gid)},
{"size", strconv.Itoa(int(h.Size))},
{"mtime", strconv.Itoa(int(h.ModTime.UTC().Unix()))},
{"typeflag", string([]byte{h.Typeflag})},
{"linkname", h.Linkname},
{"uname", h.Uname},
{"gname", h.Gname},
{"devmajor", strconv.Itoa(int(h.Devmajor))},
{"devminor", strconv.Itoa(int(h.Devminor))},
}
}

func v1TarHeaderSelect(h *tar.Header) (orderedHeaders [][2]string) {
// Get extended attributes.
xAttrKeys := make([]string, len(h.Xattrs))
for k := range h.Xattrs {
xAttrKeys = append(xAttrKeys, k)
}
sort.Strings(xAttrKeys)

// Make the slice with enough capacity to hold the 11 basic headers
// we want from the v0 selector plus however many xattrs we have.
orderedHeaders = make([][2]string, 0, 11+len(xAttrKeys))

// Copy all headers from v0 excluding the 'mtime' header (the 5th element).
v0headers := v0TarHeaderSelect(h)
orderedHeaders = append(orderedHeaders, v0headers[0:5]...)
orderedHeaders = append(orderedHeaders, v0headers[6:]...)

// Finally, append the sorted xattrs.
for _, k := range xAttrKeys {
orderedHeaders = append(orderedHeaders, [2]string{k, h.Xattrs[k]})
}

return
}

var registeredHeaderSelectors = map[Version]tarHeaderSelectFunc{
Version0: v0TarHeaderSelect,
Version1: v1TarHeaderSelect,
VersionDev: v1TarHeaderSelect,
}

func getTarHeaderSelector(v Version) (tarHeaderSelector, error) {
headerSelector, ok := registeredHeaderSelectors[v]
if !ok {
return nil, ErrVersionNotImplemented
}

return headerSelector, nil
}
8 changes: 7 additions & 1 deletion pkg/tarsum/versioning_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,17 @@ func TestVersion(t *testing.T) {
t.Errorf("expected %q, got %q", expected, v.String())
}

expected = "tarsum.dev"
expected = "tarsum.v1"
v = 1
if v.String() != expected {
t.Errorf("expected %q, got %q", expected, v.String())
}

expected = "tarsum.dev"
v = 2
if v.String() != expected {
t.Errorf("expected %q, got %q", expected, v.String())
}
}

func TestGetVersion(t *testing.T) {
Expand Down

0 comments on commit 3aa2245

Please sign in to comment.