Docker-DCO-1.1-Signed-off-by: Erik Hollensbe <github@hollensbe.org> (github: erikh)
| ... | ... |
@@ -26,6 +26,7 @@ import ( |
| 26 | 26 |
"github.com/docker/docker/pkg/parsers" |
| 27 | 27 |
"github.com/docker/docker/pkg/symlink" |
| 28 | 28 |
"github.com/docker/docker/pkg/system" |
| 29 |
+ "github.com/docker/docker/pkg/tarsum" |
|
| 29 | 30 |
"github.com/docker/docker/registry" |
| 30 | 31 |
"github.com/docker/docker/runconfig" |
| 31 | 32 |
"github.com/docker/docker/utils" |
| ... | ... |
@@ -50,7 +51,7 @@ type buildFile struct {
|
| 50 | 50 |
config *runconfig.Config |
| 51 | 51 |
|
| 52 | 52 |
contextPath string |
| 53 |
- context *utils.TarSum |
|
| 53 |
+ context *tarsum.TarSum |
|
| 54 | 54 |
|
| 55 | 55 |
verbose bool |
| 56 | 56 |
utilizeCache bool |
| ... | ... |
@@ -555,7 +556,7 @@ func (b *buildFile) runContextCommand(args string, allowRemote bool, allowDecomp |
| 555 | 555 |
if err != nil {
|
| 556 | 556 |
return err |
| 557 | 557 |
} |
| 558 |
- tarSum := &utils.TarSum{Reader: r, DisableCompression: true}
|
|
| 558 |
+ tarSum := &tarsum.TarSum{Reader: r, DisableCompression: true}
|
|
| 559 | 559 |
if _, err := io.Copy(ioutil.Discard, tarSum); err != nil {
|
| 560 | 560 |
return err |
| 561 | 561 |
} |
| ... | ... |
@@ -777,7 +778,7 @@ func (b *buildFile) Build(context io.Reader) (string, error) {
|
| 777 | 777 |
return "", err |
| 778 | 778 |
} |
| 779 | 779 |
|
| 780 |
- b.context = &utils.TarSum{Reader: decompressedStream, DisableCompression: true}
|
|
| 780 |
+ b.context = &tarsum.TarSum{Reader: decompressedStream, DisableCompression: true}
|
|
| 781 | 781 |
if err := archive.Untar(b.context, tmpdirPath, nil); err != nil {
|
| 782 | 782 |
return "", err |
| 783 | 783 |
} |
| 784 | 784 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,183 @@ |
| 0 |
+package tarsum |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bytes" |
|
| 4 |
+ "compress/gzip" |
|
| 5 |
+ "crypto/sha256" |
|
| 6 |
+ "encoding/hex" |
|
| 7 |
+ "hash" |
|
| 8 |
+ "io" |
|
| 9 |
+ "sort" |
|
| 10 |
+ "strconv" |
|
| 11 |
+ "strings" |
|
| 12 |
+ |
|
| 13 |
+ "github.com/docker/docker/utils" |
|
| 14 |
+ "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" |
|
| 15 |
+) |
|
| 16 |
+ |
|
| 17 |
+type TarSum struct {
|
|
| 18 |
+ io.Reader |
|
| 19 |
+ tarR *tar.Reader |
|
| 20 |
+ tarW *tar.Writer |
|
| 21 |
+ gz writeCloseFlusher |
|
| 22 |
+ bufTar *bytes.Buffer |
|
| 23 |
+ bufGz *bytes.Buffer |
|
| 24 |
+ h hash.Hash |
|
| 25 |
+ sums map[string]string |
|
| 26 |
+ currentFile string |
|
| 27 |
+ finished bool |
|
| 28 |
+ first bool |
|
| 29 |
+ DisableCompression bool |
|
| 30 |
+} |
|
| 31 |
+ |
|
| 32 |
+type writeCloseFlusher interface {
|
|
| 33 |
+ io.WriteCloser |
|
| 34 |
+ Flush() error |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 37 |
+type nopCloseFlusher struct {
|
|
| 38 |
+ io.Writer |
|
| 39 |
+} |
|
| 40 |
+ |
|
| 41 |
+func (n *nopCloseFlusher) Close() error {
|
|
| 42 |
+ return nil |
|
| 43 |
+} |
|
| 44 |
+ |
|
| 45 |
+func (n *nopCloseFlusher) Flush() error {
|
|
| 46 |
+ return nil |
|
| 47 |
+} |
|
| 48 |
+ |
|
| 49 |
+func (ts *TarSum) encodeHeader(h *tar.Header) error {
|
|
| 50 |
+ for _, elem := range [][2]string{
|
|
| 51 |
+ {"name", h.Name},
|
|
| 52 |
+ {"mode", strconv.Itoa(int(h.Mode))},
|
|
| 53 |
+ {"uid", strconv.Itoa(h.Uid)},
|
|
| 54 |
+ {"gid", strconv.Itoa(h.Gid)},
|
|
| 55 |
+ {"size", strconv.Itoa(int(h.Size))},
|
|
| 56 |
+ {"mtime", strconv.Itoa(int(h.ModTime.UTC().Unix()))},
|
|
| 57 |
+ {"typeflag", string([]byte{h.Typeflag})},
|
|
| 58 |
+ {"linkname", h.Linkname},
|
|
| 59 |
+ {"uname", h.Uname},
|
|
| 60 |
+ {"gname", h.Gname},
|
|
| 61 |
+ {"devmajor", strconv.Itoa(int(h.Devmajor))},
|
|
| 62 |
+ {"devminor", strconv.Itoa(int(h.Devminor))},
|
|
| 63 |
+ // {"atime", strconv.Itoa(int(h.AccessTime.UTC().Unix()))},
|
|
| 64 |
+ // {"ctime", strconv.Itoa(int(h.ChangeTime.UTC().Unix()))},
|
|
| 65 |
+ } {
|
|
| 66 |
+ if _, err := ts.h.Write([]byte(elem[0] + elem[1])); err != nil {
|
|
| 67 |
+ return err |
|
| 68 |
+ } |
|
| 69 |
+ } |
|
| 70 |
+ return nil |
|
| 71 |
+} |
|
| 72 |
+ |
|
| 73 |
+func (ts *TarSum) Read(buf []byte) (int, error) {
|
|
| 74 |
+ if ts.gz == nil {
|
|
| 75 |
+ ts.bufTar = bytes.NewBuffer([]byte{})
|
|
| 76 |
+ ts.bufGz = bytes.NewBuffer([]byte{})
|
|
| 77 |
+ ts.tarR = tar.NewReader(ts.Reader) |
|
| 78 |
+ ts.tarW = tar.NewWriter(ts.bufTar) |
|
| 79 |
+ if !ts.DisableCompression {
|
|
| 80 |
+ ts.gz = gzip.NewWriter(ts.bufGz) |
|
| 81 |
+ } else {
|
|
| 82 |
+ ts.gz = &nopCloseFlusher{Writer: ts.bufGz}
|
|
| 83 |
+ } |
|
| 84 |
+ ts.h = sha256.New() |
|
| 85 |
+ ts.h.Reset() |
|
| 86 |
+ ts.first = true |
|
| 87 |
+ ts.sums = make(map[string]string) |
|
| 88 |
+ } |
|
| 89 |
+ |
|
| 90 |
+ if ts.finished {
|
|
| 91 |
+ return ts.bufGz.Read(buf) |
|
| 92 |
+ } |
|
| 93 |
+ buf2 := make([]byte, len(buf), cap(buf)) |
|
| 94 |
+ |
|
| 95 |
+ n, err := ts.tarR.Read(buf2) |
|
| 96 |
+ if err != nil {
|
|
| 97 |
+ if err == io.EOF {
|
|
| 98 |
+ if _, err := ts.h.Write(buf2[:n]); err != nil {
|
|
| 99 |
+ return 0, err |
|
| 100 |
+ } |
|
| 101 |
+ if !ts.first {
|
|
| 102 |
+ ts.sums[ts.currentFile] = hex.EncodeToString(ts.h.Sum(nil)) |
|
| 103 |
+ ts.h.Reset() |
|
| 104 |
+ } else {
|
|
| 105 |
+ ts.first = false |
|
| 106 |
+ } |
|
| 107 |
+ |
|
| 108 |
+ currentHeader, err := ts.tarR.Next() |
|
| 109 |
+ if err != nil {
|
|
| 110 |
+ if err == io.EOF {
|
|
| 111 |
+ if err := ts.gz.Close(); err != nil {
|
|
| 112 |
+ return 0, err |
|
| 113 |
+ } |
|
| 114 |
+ ts.finished = true |
|
| 115 |
+ return n, nil |
|
| 116 |
+ } |
|
| 117 |
+ return n, err |
|
| 118 |
+ } |
|
| 119 |
+ ts.currentFile = strings.TrimSuffix(strings.TrimPrefix(currentHeader.Name, "./"), "/") |
|
| 120 |
+ if err := ts.encodeHeader(currentHeader); err != nil {
|
|
| 121 |
+ return 0, err |
|
| 122 |
+ } |
|
| 123 |
+ if err := ts.tarW.WriteHeader(currentHeader); err != nil {
|
|
| 124 |
+ return 0, err |
|
| 125 |
+ } |
|
| 126 |
+ if _, err := ts.tarW.Write(buf2[:n]); err != nil {
|
|
| 127 |
+ return 0, err |
|
| 128 |
+ } |
|
| 129 |
+ ts.tarW.Flush() |
|
| 130 |
+ if _, err := io.Copy(ts.gz, ts.bufTar); err != nil {
|
|
| 131 |
+ return 0, err |
|
| 132 |
+ } |
|
| 133 |
+ ts.gz.Flush() |
|
| 134 |
+ |
|
| 135 |
+ return ts.bufGz.Read(buf) |
|
| 136 |
+ } |
|
| 137 |
+ return n, err |
|
| 138 |
+ } |
|
| 139 |
+ |
|
| 140 |
+ // Filling the hash buffer |
|
| 141 |
+ if _, err = ts.h.Write(buf2[:n]); err != nil {
|
|
| 142 |
+ return 0, err |
|
| 143 |
+ } |
|
| 144 |
+ |
|
| 145 |
+ // Filling the tar writter |
|
| 146 |
+ if _, err = ts.tarW.Write(buf2[:n]); err != nil {
|
|
| 147 |
+ return 0, err |
|
| 148 |
+ } |
|
| 149 |
+ ts.tarW.Flush() |
|
| 150 |
+ |
|
| 151 |
+ // Filling the gz writter |
|
| 152 |
+ if _, err = io.Copy(ts.gz, ts.bufTar); err != nil {
|
|
| 153 |
+ return 0, err |
|
| 154 |
+ } |
|
| 155 |
+ ts.gz.Flush() |
|
| 156 |
+ |
|
| 157 |
+ return ts.bufGz.Read(buf) |
|
| 158 |
+} |
|
| 159 |
+ |
|
| 160 |
+func (ts *TarSum) Sum(extra []byte) string {
|
|
| 161 |
+ var sums []string |
|
| 162 |
+ |
|
| 163 |
+ for _, sum := range ts.sums {
|
|
| 164 |
+ sums = append(sums, sum) |
|
| 165 |
+ } |
|
| 166 |
+ sort.Strings(sums) |
|
| 167 |
+ h := sha256.New() |
|
| 168 |
+ if extra != nil {
|
|
| 169 |
+ h.Write(extra) |
|
| 170 |
+ } |
|
| 171 |
+ for _, sum := range sums {
|
|
| 172 |
+ utils.Debugf("-->%s<--", sum)
|
|
| 173 |
+ h.Write([]byte(sum)) |
|
| 174 |
+ } |
|
| 175 |
+ checksum := "tarsum+sha256:" + hex.EncodeToString(h.Sum(nil)) |
|
| 176 |
+ utils.Debugf("checksum processed: %s", checksum)
|
|
| 177 |
+ return checksum |
|
| 178 |
+} |
|
| 179 |
+ |
|
| 180 |
+func (ts *TarSum) GetSums() map[string]string {
|
|
| 181 |
+ return ts.sums |
|
| 182 |
+} |
| 0 | 183 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,225 @@ |
| 0 |
+package tarsum |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bytes" |
|
| 4 |
+ "crypto/rand" |
|
| 5 |
+ "fmt" |
|
| 6 |
+ "io" |
|
| 7 |
+ "io/ioutil" |
|
| 8 |
+ "os" |
|
| 9 |
+ "testing" |
|
| 10 |
+ |
|
| 11 |
+ "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" |
|
| 12 |
+) |
|
| 13 |
+ |
|
| 14 |
+type testLayer struct {
|
|
| 15 |
+ filename string |
|
| 16 |
+ options *sizedOptions |
|
| 17 |
+ jsonfile string |
|
| 18 |
+ gzip bool |
|
| 19 |
+ tarsum string |
|
| 20 |
+} |
|
| 21 |
+ |
|
| 22 |
+var testLayers = []testLayer{
|
|
| 23 |
+ {
|
|
| 24 |
+ filename: "testdata/46af0962ab5afeb5ce6740d4d91652e69206fc991fd5328c1a94d364ad00e457/layer.tar", |
|
| 25 |
+ jsonfile: "testdata/46af0962ab5afeb5ce6740d4d91652e69206fc991fd5328c1a94d364ad00e457/json", |
|
| 26 |
+ tarsum: "tarsum+sha256:e58fcf7418d4390dec8e8fb69d88c06ec07039d651fedd3aa72af9972e7d046b"}, |
|
| 27 |
+ {
|
|
| 28 |
+ filename: "testdata/46af0962ab5afeb5ce6740d4d91652e69206fc991fd5328c1a94d364ad00e457/layer.tar", |
|
| 29 |
+ jsonfile: "testdata/46af0962ab5afeb5ce6740d4d91652e69206fc991fd5328c1a94d364ad00e457/json", |
|
| 30 |
+ gzip: true, |
|
| 31 |
+ tarsum: "tarsum+sha256:e58fcf7418d4390dec8e8fb69d88c06ec07039d651fedd3aa72af9972e7d046b"}, |
|
| 32 |
+ {
|
|
| 33 |
+ filename: "testdata/511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/layer.tar", |
|
| 34 |
+ jsonfile: "testdata/511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/json", |
|
| 35 |
+ tarsum: "tarsum+sha256:ac672ee85da9ab7f9667ae3c32841d3e42f33cc52c273c23341dabba1c8b0c8b"}, |
|
| 36 |
+ {
|
|
| 37 |
+ options: &sizedOptions{1, 1024 * 1024, false, false}, // a 1mb file (in memory)
|
|
| 38 |
+ tarsum: "tarsum+sha256:8bf12d7e67c51ee2e8306cba569398b1b9f419969521a12ffb9d8875e8836738"}, |
|
| 39 |
+} |
|
| 40 |
+ |
|
| 41 |
+type sizedOptions struct {
|
|
| 42 |
+ num int64 |
|
| 43 |
+ size int64 |
|
| 44 |
+ isRand bool |
|
| 45 |
+ realFile bool |
|
| 46 |
+} |
|
| 47 |
+ |
|
| 48 |
+// make a tar: |
|
| 49 |
+// * num is the number of files the tar should have |
|
| 50 |
+// * size is the bytes per file |
|
| 51 |
+// * isRand is whether the contents of the files should be a random chunk (otherwise it's all zeros) |
|
| 52 |
+// * realFile will write to a TempFile, instead of an in memory buffer |
|
| 53 |
+func sizedTar(opts sizedOptions) io.Reader {
|
|
| 54 |
+ var ( |
|
| 55 |
+ fh io.ReadWriter |
|
| 56 |
+ err error |
|
| 57 |
+ ) |
|
| 58 |
+ if opts.realFile {
|
|
| 59 |
+ fh, err = ioutil.TempFile("", "tarsum")
|
|
| 60 |
+ if err != nil {
|
|
| 61 |
+ return nil |
|
| 62 |
+ } |
|
| 63 |
+ } else {
|
|
| 64 |
+ fh = bytes.NewBuffer([]byte{})
|
|
| 65 |
+ } |
|
| 66 |
+ tarW := tar.NewWriter(fh) |
|
| 67 |
+ for i := int64(0); i < opts.num; i++ {
|
|
| 68 |
+ err := tarW.WriteHeader(&tar.Header{
|
|
| 69 |
+ Name: fmt.Sprintf("/testdata%d", i),
|
|
| 70 |
+ Mode: 0755, |
|
| 71 |
+ Uid: 0, |
|
| 72 |
+ Gid: 0, |
|
| 73 |
+ Size: opts.size, |
|
| 74 |
+ }) |
|
| 75 |
+ if err != nil {
|
|
| 76 |
+ return nil |
|
| 77 |
+ } |
|
| 78 |
+ var rBuf []byte |
|
| 79 |
+ if opts.isRand {
|
|
| 80 |
+ rBuf = make([]byte, 8) |
|
| 81 |
+ _, err = rand.Read(rBuf) |
|
| 82 |
+ if err != nil {
|
|
| 83 |
+ return nil |
|
| 84 |
+ } |
|
| 85 |
+ } else {
|
|
| 86 |
+ rBuf = []byte{0, 0, 0, 0, 0, 0, 0, 0}
|
|
| 87 |
+ } |
|
| 88 |
+ |
|
| 89 |
+ for i := int64(0); i < opts.size/int64(8); i++ {
|
|
| 90 |
+ tarW.Write(rBuf) |
|
| 91 |
+ } |
|
| 92 |
+ } |
|
| 93 |
+ return fh |
|
| 94 |
+} |
|
| 95 |
+ |
|
| 96 |
+func TestTarSums(t *testing.T) {
|
|
| 97 |
+ for _, layer := range testLayers {
|
|
| 98 |
+ var ( |
|
| 99 |
+ fh io.Reader |
|
| 100 |
+ err error |
|
| 101 |
+ ) |
|
| 102 |
+ if len(layer.filename) > 0 {
|
|
| 103 |
+ fh, err = os.Open(layer.filename) |
|
| 104 |
+ if err != nil {
|
|
| 105 |
+ t.Errorf("failed to open %s: %s", layer.filename, err)
|
|
| 106 |
+ continue |
|
| 107 |
+ } |
|
| 108 |
+ } else if layer.options != nil {
|
|
| 109 |
+ fh = sizedTar(*layer.options) |
|
| 110 |
+ } else {
|
|
| 111 |
+ // What else is there to test? |
|
| 112 |
+ t.Errorf("what to do with %#v", layer)
|
|
| 113 |
+ continue |
|
| 114 |
+ } |
|
| 115 |
+ if file, ok := fh.(*os.File); ok {
|
|
| 116 |
+ defer file.Close() |
|
| 117 |
+ } |
|
| 118 |
+ |
|
| 119 |
+ // double negatives! |
|
| 120 |
+ ts := &TarSum{Reader: fh, DisableCompression: !layer.gzip}
|
|
| 121 |
+ _, err = io.Copy(ioutil.Discard, ts) |
|
| 122 |
+ if err != nil {
|
|
| 123 |
+ t.Errorf("failed to copy from %s: %s", layer.filename, err)
|
|
| 124 |
+ continue |
|
| 125 |
+ } |
|
| 126 |
+ var gotSum string |
|
| 127 |
+ if len(layer.jsonfile) > 0 {
|
|
| 128 |
+ jfh, err := os.Open(layer.jsonfile) |
|
| 129 |
+ if err != nil {
|
|
| 130 |
+ t.Errorf("failed to open %s: %s", layer.jsonfile, err)
|
|
| 131 |
+ continue |
|
| 132 |
+ } |
|
| 133 |
+ buf, err := ioutil.ReadAll(jfh) |
|
| 134 |
+ if err != nil {
|
|
| 135 |
+ t.Errorf("failed to readAll %s: %s", layer.jsonfile, err)
|
|
| 136 |
+ continue |
|
| 137 |
+ } |
|
| 138 |
+ gotSum = ts.Sum(buf) |
|
| 139 |
+ } else {
|
|
| 140 |
+ gotSum = ts.Sum(nil) |
|
| 141 |
+ } |
|
| 142 |
+ |
|
| 143 |
+ if layer.tarsum != gotSum {
|
|
| 144 |
+ t.Errorf("expecting [%s], but got [%s]", layer.tarsum, gotSum)
|
|
| 145 |
+ } |
|
| 146 |
+ } |
|
| 147 |
+} |
|
| 148 |
+ |
|
| 149 |
+func Benchmark9kTar(b *testing.B) {
|
|
| 150 |
+ buf := bytes.NewBuffer([]byte{})
|
|
| 151 |
+ fh, err := os.Open("testdata/46af0962ab5afeb5ce6740d4d91652e69206fc991fd5328c1a94d364ad00e457/layer.tar")
|
|
| 152 |
+ if err != nil {
|
|
| 153 |
+ b.Error(err) |
|
| 154 |
+ return |
|
| 155 |
+ } |
|
| 156 |
+ n, err := io.Copy(buf, fh) |
|
| 157 |
+ fh.Close() |
|
| 158 |
+ |
|
| 159 |
+ b.SetBytes(n) |
|
| 160 |
+ b.ResetTimer() |
|
| 161 |
+ for i := 0; i < b.N; i++ {
|
|
| 162 |
+ ts := &TarSum{Reader: buf, DisableCompression: true}
|
|
| 163 |
+ io.Copy(ioutil.Discard, ts) |
|
| 164 |
+ ts.Sum(nil) |
|
| 165 |
+ } |
|
| 166 |
+} |
|
| 167 |
+ |
|
| 168 |
+func Benchmark9kTarGzip(b *testing.B) {
|
|
| 169 |
+ buf := bytes.NewBuffer([]byte{})
|
|
| 170 |
+ fh, err := os.Open("testdata/46af0962ab5afeb5ce6740d4d91652e69206fc991fd5328c1a94d364ad00e457/layer.tar")
|
|
| 171 |
+ if err != nil {
|
|
| 172 |
+ b.Error(err) |
|
| 173 |
+ return |
|
| 174 |
+ } |
|
| 175 |
+ n, err := io.Copy(buf, fh) |
|
| 176 |
+ fh.Close() |
|
| 177 |
+ |
|
| 178 |
+ b.SetBytes(n) |
|
| 179 |
+ b.ResetTimer() |
|
| 180 |
+ for i := 0; i < b.N; i++ {
|
|
| 181 |
+ ts := &TarSum{Reader: buf, DisableCompression: false}
|
|
| 182 |
+ io.Copy(ioutil.Discard, ts) |
|
| 183 |
+ ts.Sum(nil) |
|
| 184 |
+ } |
|
| 185 |
+} |
|
| 186 |
+ |
|
| 187 |
+// this is a single big file in the tar archive |
|
| 188 |
+func Benchmark1mbSingleFileTar(b *testing.B) {
|
|
| 189 |
+ benchmarkTar(b, sizedOptions{1, 1024 * 1024, true, true}, false)
|
|
| 190 |
+} |
|
| 191 |
+ |
|
| 192 |
+// this is a single big file in the tar archive |
|
| 193 |
+func Benchmark1mbSingleFileTarGzip(b *testing.B) {
|
|
| 194 |
+ benchmarkTar(b, sizedOptions{1, 1024 * 1024, true, true}, true)
|
|
| 195 |
+} |
|
| 196 |
+ |
|
| 197 |
+// this is 1024 1k files in the tar archive |
|
| 198 |
+func Benchmark1kFilesTar(b *testing.B) {
|
|
| 199 |
+ benchmarkTar(b, sizedOptions{1024, 1024, true, true}, false)
|
|
| 200 |
+} |
|
| 201 |
+ |
|
| 202 |
+// this is 1024 1k files in the tar archive |
|
| 203 |
+func Benchmark1kFilesTarGzip(b *testing.B) {
|
|
| 204 |
+ benchmarkTar(b, sizedOptions{1024, 1024, true, true}, true)
|
|
| 205 |
+} |
|
| 206 |
+ |
|
| 207 |
+func benchmarkTar(b *testing.B, opts sizedOptions, isGzip bool) {
|
|
| 208 |
+ var fh *os.File |
|
| 209 |
+ tarReader := sizedTar(opts) |
|
| 210 |
+ if br, ok := tarReader.(*os.File); ok {
|
|
| 211 |
+ fh = br |
|
| 212 |
+ } |
|
| 213 |
+ defer os.Remove(fh.Name()) |
|
| 214 |
+ defer fh.Close() |
|
| 215 |
+ |
|
| 216 |
+ b.SetBytes(opts.size * opts.num) |
|
| 217 |
+ b.ResetTimer() |
|
| 218 |
+ for i := 0; i < b.N; i++ {
|
|
| 219 |
+ ts := &TarSum{Reader: fh, DisableCompression: !isGzip}
|
|
| 220 |
+ io.Copy(ioutil.Discard, ts) |
|
| 221 |
+ ts.Sum(nil) |
|
| 222 |
+ fh.Seek(0, 0) |
|
| 223 |
+ } |
|
| 224 |
+} |
| ... | ... |
@@ -26,6 +26,7 @@ import ( |
| 26 | 26 |
"github.com/docker/docker/dockerversion" |
| 27 | 27 |
"github.com/docker/docker/pkg/httputils" |
| 28 | 28 |
"github.com/docker/docker/pkg/parsers/kernel" |
| 29 |
+ "github.com/docker/docker/pkg/tarsum" |
|
| 29 | 30 |
"github.com/docker/docker/utils" |
| 30 | 31 |
) |
| 31 | 32 |
|
| ... | ... |
@@ -638,7 +639,7 @@ func (r *Registry) PushImageLayerRegistry(imgID string, layer io.Reader, registr |
| 638 | 638 |
|
| 639 | 639 |
utils.Debugf("[registry] Calling PUT %s", registry+"images/"+imgID+"/layer")
|
| 640 | 640 |
|
| 641 |
- tarsumLayer := &utils.TarSum{Reader: layer}
|
|
| 641 |
+ tarsumLayer := &tarsum.TarSum{Reader: layer}
|
|
| 642 | 642 |
h := sha256.New() |
| 643 | 643 |
h.Write(jsonRaw) |
| 644 | 644 |
h.Write([]byte{'\n'})
|
| 645 | 645 |
deleted file mode 100644 |
| ... | ... |
@@ -1,181 +0,0 @@ |
| 1 |
-package utils |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bytes" |
|
| 5 |
- "compress/gzip" |
|
| 6 |
- "crypto/sha256" |
|
| 7 |
- "encoding/hex" |
|
| 8 |
- "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" |
|
| 9 |
- "hash" |
|
| 10 |
- "io" |
|
| 11 |
- "sort" |
|
| 12 |
- "strconv" |
|
| 13 |
- "strings" |
|
| 14 |
-) |
|
| 15 |
- |
|
| 16 |
-type TarSum struct {
|
|
| 17 |
- io.Reader |
|
| 18 |
- tarR *tar.Reader |
|
| 19 |
- tarW *tar.Writer |
|
| 20 |
- gz writeCloseFlusher |
|
| 21 |
- bufTar *bytes.Buffer |
|
| 22 |
- bufGz *bytes.Buffer |
|
| 23 |
- h hash.Hash |
|
| 24 |
- sums map[string]string |
|
| 25 |
- currentFile string |
|
| 26 |
- finished bool |
|
| 27 |
- first bool |
|
| 28 |
- DisableCompression bool |
|
| 29 |
-} |
|
| 30 |
- |
|
| 31 |
-type writeCloseFlusher interface {
|
|
| 32 |
- io.WriteCloser |
|
| 33 |
- Flush() error |
|
| 34 |
-} |
|
| 35 |
- |
|
| 36 |
-type nopCloseFlusher struct {
|
|
| 37 |
- io.Writer |
|
| 38 |
-} |
|
| 39 |
- |
|
| 40 |
-func (n *nopCloseFlusher) Close() error {
|
|
| 41 |
- return nil |
|
| 42 |
-} |
|
| 43 |
- |
|
| 44 |
-func (n *nopCloseFlusher) Flush() error {
|
|
| 45 |
- return nil |
|
| 46 |
-} |
|
| 47 |
- |
|
| 48 |
-func (ts *TarSum) encodeHeader(h *tar.Header) error {
|
|
| 49 |
- for _, elem := range [][2]string{
|
|
| 50 |
- {"name", h.Name},
|
|
| 51 |
- {"mode", strconv.Itoa(int(h.Mode))},
|
|
| 52 |
- {"uid", strconv.Itoa(h.Uid)},
|
|
| 53 |
- {"gid", strconv.Itoa(h.Gid)},
|
|
| 54 |
- {"size", strconv.Itoa(int(h.Size))},
|
|
| 55 |
- {"mtime", strconv.Itoa(int(h.ModTime.UTC().Unix()))},
|
|
| 56 |
- {"typeflag", string([]byte{h.Typeflag})},
|
|
| 57 |
- {"linkname", h.Linkname},
|
|
| 58 |
- {"uname", h.Uname},
|
|
| 59 |
- {"gname", h.Gname},
|
|
| 60 |
- {"devmajor", strconv.Itoa(int(h.Devmajor))},
|
|
| 61 |
- {"devminor", strconv.Itoa(int(h.Devminor))},
|
|
| 62 |
- // {"atime", strconv.Itoa(int(h.AccessTime.UTC().Unix()))},
|
|
| 63 |
- // {"ctime", strconv.Itoa(int(h.ChangeTime.UTC().Unix()))},
|
|
| 64 |
- } {
|
|
| 65 |
- if _, err := ts.h.Write([]byte(elem[0] + elem[1])); err != nil {
|
|
| 66 |
- return err |
|
| 67 |
- } |
|
| 68 |
- } |
|
| 69 |
- return nil |
|
| 70 |
-} |
|
| 71 |
- |
|
| 72 |
-func (ts *TarSum) Read(buf []byte) (int, error) {
|
|
| 73 |
- if ts.gz == nil {
|
|
| 74 |
- ts.bufTar = bytes.NewBuffer([]byte{})
|
|
| 75 |
- ts.bufGz = bytes.NewBuffer([]byte{})
|
|
| 76 |
- ts.tarR = tar.NewReader(ts.Reader) |
|
| 77 |
- ts.tarW = tar.NewWriter(ts.bufTar) |
|
| 78 |
- if !ts.DisableCompression {
|
|
| 79 |
- ts.gz = gzip.NewWriter(ts.bufGz) |
|
| 80 |
- } else {
|
|
| 81 |
- ts.gz = &nopCloseFlusher{Writer: ts.bufGz}
|
|
| 82 |
- } |
|
| 83 |
- ts.h = sha256.New() |
|
| 84 |
- ts.h.Reset() |
|
| 85 |
- ts.first = true |
|
| 86 |
- ts.sums = make(map[string]string) |
|
| 87 |
- } |
|
| 88 |
- |
|
| 89 |
- if ts.finished {
|
|
| 90 |
- return ts.bufGz.Read(buf) |
|
| 91 |
- } |
|
| 92 |
- buf2 := make([]byte, len(buf), cap(buf)) |
|
| 93 |
- |
|
| 94 |
- n, err := ts.tarR.Read(buf2) |
|
| 95 |
- if err != nil {
|
|
| 96 |
- if err == io.EOF {
|
|
| 97 |
- if _, err := ts.h.Write(buf2[:n]); err != nil {
|
|
| 98 |
- return 0, err |
|
| 99 |
- } |
|
| 100 |
- if !ts.first {
|
|
| 101 |
- ts.sums[ts.currentFile] = hex.EncodeToString(ts.h.Sum(nil)) |
|
| 102 |
- ts.h.Reset() |
|
| 103 |
- } else {
|
|
| 104 |
- ts.first = false |
|
| 105 |
- } |
|
| 106 |
- |
|
| 107 |
- currentHeader, err := ts.tarR.Next() |
|
| 108 |
- if err != nil {
|
|
| 109 |
- if err == io.EOF {
|
|
| 110 |
- if err := ts.gz.Close(); err != nil {
|
|
| 111 |
- return 0, err |
|
| 112 |
- } |
|
| 113 |
- ts.finished = true |
|
| 114 |
- return n, nil |
|
| 115 |
- } |
|
| 116 |
- return n, err |
|
| 117 |
- } |
|
| 118 |
- ts.currentFile = strings.TrimSuffix(strings.TrimPrefix(currentHeader.Name, "./"), "/") |
|
| 119 |
- if err := ts.encodeHeader(currentHeader); err != nil {
|
|
| 120 |
- return 0, err |
|
| 121 |
- } |
|
| 122 |
- if err := ts.tarW.WriteHeader(currentHeader); err != nil {
|
|
| 123 |
- return 0, err |
|
| 124 |
- } |
|
| 125 |
- if _, err := ts.tarW.Write(buf2[:n]); err != nil {
|
|
| 126 |
- return 0, err |
|
| 127 |
- } |
|
| 128 |
- ts.tarW.Flush() |
|
| 129 |
- if _, err := io.Copy(ts.gz, ts.bufTar); err != nil {
|
|
| 130 |
- return 0, err |
|
| 131 |
- } |
|
| 132 |
- ts.gz.Flush() |
|
| 133 |
- |
|
| 134 |
- return ts.bufGz.Read(buf) |
|
| 135 |
- } |
|
| 136 |
- return n, err |
|
| 137 |
- } |
|
| 138 |
- |
|
| 139 |
- // Filling the hash buffer |
|
| 140 |
- if _, err = ts.h.Write(buf2[:n]); err != nil {
|
|
| 141 |
- return 0, err |
|
| 142 |
- } |
|
| 143 |
- |
|
| 144 |
- // Filling the tar writter |
|
| 145 |
- if _, err = ts.tarW.Write(buf2[:n]); err != nil {
|
|
| 146 |
- return 0, err |
|
| 147 |
- } |
|
| 148 |
- ts.tarW.Flush() |
|
| 149 |
- |
|
| 150 |
- // Filling the gz writter |
|
| 151 |
- if _, err = io.Copy(ts.gz, ts.bufTar); err != nil {
|
|
| 152 |
- return 0, err |
|
| 153 |
- } |
|
| 154 |
- ts.gz.Flush() |
|
| 155 |
- |
|
| 156 |
- return ts.bufGz.Read(buf) |
|
| 157 |
-} |
|
| 158 |
- |
|
| 159 |
-func (ts *TarSum) Sum(extra []byte) string {
|
|
| 160 |
- var sums []string |
|
| 161 |
- |
|
| 162 |
- for _, sum := range ts.sums {
|
|
| 163 |
- sums = append(sums, sum) |
|
| 164 |
- } |
|
| 165 |
- sort.Strings(sums) |
|
| 166 |
- h := sha256.New() |
|
| 167 |
- if extra != nil {
|
|
| 168 |
- h.Write(extra) |
|
| 169 |
- } |
|
| 170 |
- for _, sum := range sums {
|
|
| 171 |
- Debugf("-->%s<--", sum)
|
|
| 172 |
- h.Write([]byte(sum)) |
|
| 173 |
- } |
|
| 174 |
- checksum := "tarsum+sha256:" + hex.EncodeToString(h.Sum(nil)) |
|
| 175 |
- Debugf("checksum processed: %s", checksum)
|
|
| 176 |
- return checksum |
|
| 177 |
-} |
|
| 178 |
- |
|
| 179 |
-func (ts *TarSum) GetSums() map[string]string {
|
|
| 180 |
- return ts.sums |
|
| 181 |
-} |
| 182 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,225 +0,0 @@ |
| 1 |
-package utils |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bytes" |
|
| 5 |
- "crypto/rand" |
|
| 6 |
- "fmt" |
|
| 7 |
- "io" |
|
| 8 |
- "io/ioutil" |
|
| 9 |
- "os" |
|
| 10 |
- "testing" |
|
| 11 |
- |
|
| 12 |
- "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar" |
|
| 13 |
-) |
|
| 14 |
- |
|
| 15 |
-type testLayer struct {
|
|
| 16 |
- filename string |
|
| 17 |
- options *sizedOptions |
|
| 18 |
- jsonfile string |
|
| 19 |
- gzip bool |
|
| 20 |
- tarsum string |
|
| 21 |
-} |
|
| 22 |
- |
|
| 23 |
-var testLayers = []testLayer{
|
|
| 24 |
- {
|
|
| 25 |
- filename: "testdata/46af0962ab5afeb5ce6740d4d91652e69206fc991fd5328c1a94d364ad00e457/layer.tar", |
|
| 26 |
- jsonfile: "testdata/46af0962ab5afeb5ce6740d4d91652e69206fc991fd5328c1a94d364ad00e457/json", |
|
| 27 |
- tarsum: "tarsum+sha256:e58fcf7418d4390dec8e8fb69d88c06ec07039d651fedd3aa72af9972e7d046b"}, |
|
| 28 |
- {
|
|
| 29 |
- filename: "testdata/46af0962ab5afeb5ce6740d4d91652e69206fc991fd5328c1a94d364ad00e457/layer.tar", |
|
| 30 |
- jsonfile: "testdata/46af0962ab5afeb5ce6740d4d91652e69206fc991fd5328c1a94d364ad00e457/json", |
|
| 31 |
- gzip: true, |
|
| 32 |
- tarsum: "tarsum+sha256:e58fcf7418d4390dec8e8fb69d88c06ec07039d651fedd3aa72af9972e7d046b"}, |
|
| 33 |
- {
|
|
| 34 |
- filename: "testdata/511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/layer.tar", |
|
| 35 |
- jsonfile: "testdata/511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/json", |
|
| 36 |
- tarsum: "tarsum+sha256:ac672ee85da9ab7f9667ae3c32841d3e42f33cc52c273c23341dabba1c8b0c8b"}, |
|
| 37 |
- {
|
|
| 38 |
- options: &sizedOptions{1, 1024 * 1024, false, false}, // a 1mb file (in memory)
|
|
| 39 |
- tarsum: "tarsum+sha256:8bf12d7e67c51ee2e8306cba569398b1b9f419969521a12ffb9d8875e8836738"}, |
|
| 40 |
-} |
|
| 41 |
- |
|
| 42 |
-type sizedOptions struct {
|
|
| 43 |
- num int64 |
|
| 44 |
- size int64 |
|
| 45 |
- isRand bool |
|
| 46 |
- realFile bool |
|
| 47 |
-} |
|
| 48 |
- |
|
| 49 |
-// make a tar: |
|
| 50 |
-// * num is the number of files the tar should have |
|
| 51 |
-// * size is the bytes per file |
|
| 52 |
-// * isRand is whether the contents of the files should be a random chunk (otherwise it's all zeros) |
|
| 53 |
-// * realFile will write to a TempFile, instead of an in memory buffer |
|
| 54 |
-func sizedTar(opts sizedOptions) io.Reader {
|
|
| 55 |
- var ( |
|
| 56 |
- fh io.ReadWriter |
|
| 57 |
- err error |
|
| 58 |
- ) |
|
| 59 |
- if opts.realFile {
|
|
| 60 |
- fh, err = ioutil.TempFile("", "tarsum")
|
|
| 61 |
- if err != nil {
|
|
| 62 |
- return nil |
|
| 63 |
- } |
|
| 64 |
- } else {
|
|
| 65 |
- fh = bytes.NewBuffer([]byte{})
|
|
| 66 |
- } |
|
| 67 |
- tarW := tar.NewWriter(fh) |
|
| 68 |
- for i := int64(0); i < opts.num; i++ {
|
|
| 69 |
- err := tarW.WriteHeader(&tar.Header{
|
|
| 70 |
- Name: fmt.Sprintf("/testdata%d", i),
|
|
| 71 |
- Mode: 0755, |
|
| 72 |
- Uid: 0, |
|
| 73 |
- Gid: 0, |
|
| 74 |
- Size: opts.size, |
|
| 75 |
- }) |
|
| 76 |
- if err != nil {
|
|
| 77 |
- return nil |
|
| 78 |
- } |
|
| 79 |
- var rBuf []byte |
|
| 80 |
- if opts.isRand {
|
|
| 81 |
- rBuf = make([]byte, 8) |
|
| 82 |
- _, err = rand.Read(rBuf) |
|
| 83 |
- if err != nil {
|
|
| 84 |
- return nil |
|
| 85 |
- } |
|
| 86 |
- } else {
|
|
| 87 |
- rBuf = []byte{0, 0, 0, 0, 0, 0, 0, 0}
|
|
| 88 |
- } |
|
| 89 |
- |
|
| 90 |
- for i := int64(0); i < opts.size/int64(8); i++ {
|
|
| 91 |
- tarW.Write(rBuf) |
|
| 92 |
- } |
|
| 93 |
- } |
|
| 94 |
- return fh |
|
| 95 |
-} |
|
| 96 |
- |
|
| 97 |
-func TestTarSums(t *testing.T) {
|
|
| 98 |
- for _, layer := range testLayers {
|
|
| 99 |
- var ( |
|
| 100 |
- fh io.Reader |
|
| 101 |
- err error |
|
| 102 |
- ) |
|
| 103 |
- if len(layer.filename) > 0 {
|
|
| 104 |
- fh, err = os.Open(layer.filename) |
|
| 105 |
- if err != nil {
|
|
| 106 |
- t.Errorf("failed to open %s: %s", layer.filename, err)
|
|
| 107 |
- continue |
|
| 108 |
- } |
|
| 109 |
- } else if layer.options != nil {
|
|
| 110 |
- fh = sizedTar(*layer.options) |
|
| 111 |
- } else {
|
|
| 112 |
- // What else is there to test? |
|
| 113 |
- t.Errorf("what to do with %#v", layer)
|
|
| 114 |
- continue |
|
| 115 |
- } |
|
| 116 |
- if file, ok := fh.(*os.File); ok {
|
|
| 117 |
- defer file.Close() |
|
| 118 |
- } |
|
| 119 |
- |
|
| 120 |
- // double negatives! |
|
| 121 |
- ts := &TarSum{Reader: fh, DisableCompression: !layer.gzip}
|
|
| 122 |
- _, err = io.Copy(ioutil.Discard, ts) |
|
| 123 |
- if err != nil {
|
|
| 124 |
- t.Errorf("failed to copy from %s: %s", layer.filename, err)
|
|
| 125 |
- continue |
|
| 126 |
- } |
|
| 127 |
- var gotSum string |
|
| 128 |
- if len(layer.jsonfile) > 0 {
|
|
| 129 |
- jfh, err := os.Open(layer.jsonfile) |
|
| 130 |
- if err != nil {
|
|
| 131 |
- t.Errorf("failed to open %s: %s", layer.jsonfile, err)
|
|
| 132 |
- continue |
|
| 133 |
- } |
|
| 134 |
- buf, err := ioutil.ReadAll(jfh) |
|
| 135 |
- if err != nil {
|
|
| 136 |
- t.Errorf("failed to readAll %s: %s", layer.jsonfile, err)
|
|
| 137 |
- continue |
|
| 138 |
- } |
|
| 139 |
- gotSum = ts.Sum(buf) |
|
| 140 |
- } else {
|
|
| 141 |
- gotSum = ts.Sum(nil) |
|
| 142 |
- } |
|
| 143 |
- |
|
| 144 |
- if layer.tarsum != gotSum {
|
|
| 145 |
- t.Errorf("expecting [%s], but got [%s]", layer.tarsum, gotSum)
|
|
| 146 |
- } |
|
| 147 |
- } |
|
| 148 |
-} |
|
| 149 |
- |
|
| 150 |
-func Benchmark9kTar(b *testing.B) {
|
|
| 151 |
- buf := bytes.NewBuffer([]byte{})
|
|
| 152 |
- fh, err := os.Open("testdata/46af0962ab5afeb5ce6740d4d91652e69206fc991fd5328c1a94d364ad00e457/layer.tar")
|
|
| 153 |
- if err != nil {
|
|
| 154 |
- b.Error(err) |
|
| 155 |
- return |
|
| 156 |
- } |
|
| 157 |
- n, err := io.Copy(buf, fh) |
|
| 158 |
- fh.Close() |
|
| 159 |
- |
|
| 160 |
- b.SetBytes(n) |
|
| 161 |
- b.ResetTimer() |
|
| 162 |
- for i := 0; i < b.N; i++ {
|
|
| 163 |
- ts := &TarSum{Reader: buf, DisableCompression: true}
|
|
| 164 |
- io.Copy(ioutil.Discard, ts) |
|
| 165 |
- ts.Sum(nil) |
|
| 166 |
- } |
|
| 167 |
-} |
|
| 168 |
- |
|
| 169 |
-func Benchmark9kTarGzip(b *testing.B) {
|
|
| 170 |
- buf := bytes.NewBuffer([]byte{})
|
|
| 171 |
- fh, err := os.Open("testdata/46af0962ab5afeb5ce6740d4d91652e69206fc991fd5328c1a94d364ad00e457/layer.tar")
|
|
| 172 |
- if err != nil {
|
|
| 173 |
- b.Error(err) |
|
| 174 |
- return |
|
| 175 |
- } |
|
| 176 |
- n, err := io.Copy(buf, fh) |
|
| 177 |
- fh.Close() |
|
| 178 |
- |
|
| 179 |
- b.SetBytes(n) |
|
| 180 |
- b.ResetTimer() |
|
| 181 |
- for i := 0; i < b.N; i++ {
|
|
| 182 |
- ts := &TarSum{Reader: buf, DisableCompression: false}
|
|
| 183 |
- io.Copy(ioutil.Discard, ts) |
|
| 184 |
- ts.Sum(nil) |
|
| 185 |
- } |
|
| 186 |
-} |
|
| 187 |
- |
|
| 188 |
-// this is a single big file in the tar archive |
|
| 189 |
-func Benchmark1mbSingleFileTar(b *testing.B) {
|
|
| 190 |
- benchmarkTar(b, sizedOptions{1, 1024 * 1024, true, true}, false)
|
|
| 191 |
-} |
|
| 192 |
- |
|
| 193 |
-// this is a single big file in the tar archive |
|
| 194 |
-func Benchmark1mbSingleFileTarGzip(b *testing.B) {
|
|
| 195 |
- benchmarkTar(b, sizedOptions{1, 1024 * 1024, true, true}, true)
|
|
| 196 |
-} |
|
| 197 |
- |
|
| 198 |
-// this is 1024 1k files in the tar archive |
|
| 199 |
-func Benchmark1kFilesTar(b *testing.B) {
|
|
| 200 |
- benchmarkTar(b, sizedOptions{1024, 1024, true, true}, false)
|
|
| 201 |
-} |
|
| 202 |
- |
|
| 203 |
-// this is 1024 1k files in the tar archive |
|
| 204 |
-func Benchmark1kFilesTarGzip(b *testing.B) {
|
|
| 205 |
- benchmarkTar(b, sizedOptions{1024, 1024, true, true}, true)
|
|
| 206 |
-} |
|
| 207 |
- |
|
| 208 |
-func benchmarkTar(b *testing.B, opts sizedOptions, isGzip bool) {
|
|
| 209 |
- var fh *os.File |
|
| 210 |
- tarReader := sizedTar(opts) |
|
| 211 |
- if br, ok := tarReader.(*os.File); ok {
|
|
| 212 |
- fh = br |
|
| 213 |
- } |
|
| 214 |
- defer os.Remove(fh.Name()) |
|
| 215 |
- defer fh.Close() |
|
| 216 |
- |
|
| 217 |
- b.SetBytes(opts.size * opts.num) |
|
| 218 |
- b.ResetTimer() |
|
| 219 |
- for i := 0; i < b.N; i++ {
|
|
| 220 |
- ts := &TarSum{Reader: fh, DisableCompression: !isGzip}
|
|
| 221 |
- io.Copy(ioutil.Discard, ts) |
|
| 222 |
- ts.Sum(nil) |
|
| 223 |
- fh.Seek(0, 0) |
|
| 224 |
- } |
|
| 225 |
-} |